-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.xml
More file actions
651 lines (651 loc) · 50.9 KB
/
index.xml
File metadata and controls
651 lines (651 loc) · 50.9 KB
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
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>somethingSTRANGE</title><link>https://somethingstrange.com/</link><description>Recent content on somethingSTRANGE</description><generator>Hugo -- gohugo.io</generator><language>en</language><copyright>©2025. All content is licensed under<a target='_blank' rel='external noopener' href='https://www.apache.org/licenses/LICENSE-2.0'>Apache License 2.0</a>.</copyright><lastBuildDate>Fri, 20 Jun 2025 14:20:00 -0700</lastBuildDate><atom:link href="https://somethingstrange.com/index.xml" rel="self" type="application/rss+xml"/><item><title>RegEx Cheatsheet</title><link>https://somethingstrange.com/posts/regex-cheatsheet/</link><pubDate>Fri, 20 Jun 2025 14:20:00 -0700</pubDate><atom:modified>Fri, 20 Jun 2025 17:07:15 -0700</atom:modified><guid>https://somethingstrange.com/posts/regex-cheatsheet/</guid><description>A handy cheatsheet and overview of the syntax for various regular expression flavors and implementations.</description><dc:creator>Michael Ryan</dc:creator><category>regex</category><category>gamedev</category><category>webdev</category></item><item><title>Got Tabs?</title><link>https://somethingstrange.com/posts/got-tabs/</link><pubDate>Sat, 15 Feb 2025 10:28:45 -0800</pubDate><atom:modified>Mon, 17 Feb 2025 01:47:39 -0800</atom:modified><guid>https://somethingstrange.com/posts/got-tabs/</guid><description><p>Hey there! Ever find yourself wanting a simple way to create tab spacers without resorting to browser extensions? That was me a while back, and that&rsquo;s when the idea for this little <a href="https://somethingstrange.com/tab/">Tab</a> app popped into my head. It started with a simple goal: to add tab bar spaces without titles. But as I tinkered and tailored it, it blossomed into something much more robust and versatile. Let me take you through its journey and highlight the cool features it now boasts.</p></description><dc:creator>Michael Ryan</dc:creator><category>app</category><category>browser</category><category>customization</category><category>icons</category><category>markdown</category><category>productivity</category><category>tabs</category><category>general</category></item><item><title>Tab</title><link>https://somethingstrange.com/tab/</link><pubDate>Sat, 15 Feb 2025 08:20:45 -0800</pubDate><atom:modified>Mon, 17 Feb 2025 02:41:51 -0800</atom:modified><guid>https://somethingstrange.com/tab/</guid><description><div id="tab-panes">
<div id="tab-pane-toolbar">
<div data-mode="0" id="tab-mode">
<button id="mode-edit-disabled" onclick="editEnabled()">
<span style="line-height:1em; vertical-align:middle;"><svg style="height:1em; width:1em" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#duotone-caret-square-right"/></svg></span>
</button>
<button id="mode-edit-enabled" onclick="editDisabled()">
<span style="line-height:1em; vertical-align:middle;"><svg style="height:1em; width:1em" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#duotone-caret-square-down"/></svg></span>
</button>
</div>
<div data-dirty="0" id="tab-state">
<div id="state-dirty">
<span style="line-height:1em; vertical-align:middle;"><svg style="height:1em; width:1em" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#duotone-edit"/></svg></span>
<div class="state-label">Edited</div>
</div>
<div id="state-saved">
<span style="line-height:1em; vertical-align:middle;"><svg style="height:1em; width:1em" viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#duotone-save"/></svg></span>
<div class="state-label">Saved</div>
</div>
</div>
<div class="flex-grow"></div>
<div class="site-logo">
<a class="site-link" href="https://somethingstrange.com/" rel="home" title="somethingSTRANGE">
<span class="logo-svg"><svg height="96" viewBox="0 0 128 96" width="128" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m36 32c0-16 12-28 28-28s28 12 28 28c0 8-2 12-2 12s-2 6.25-2 8.125 1 3.875 3 5.875 3 2.5 5 3h1e-4c2 0.5 3.9999 1 5.9999 0 1-0.5 2-1.375 3-2.25s2-1.75 3-2.25c2-1 3.5-1.5 6-1.5s7 2 7 5-1 5-4 7c-2 1.5-3.5 1-4 0.5s-1-1.5-1-1.5 2 0 3-0.5 2-2 2-4-1-2.5-3-2.5-4 1-7 4-4.5 4-6 4.5-4.5 0.5-7-0.5-7-3-7-3 2 3 3 7 7 5 9.5 5 7-0.5 8.5-2 1.5-3 1.5-3l0.5 1s0.5 1.5-0.5 3.5-5.5 3.5-10 4-9 0.5-11.5-1.5c-1.2978-1.0382-1.7871-2.3459-2.3773-3.9231-0.5467-1.461-1.18-3.1533-2.6227-5.0769-2.3858-3.1811-2.8743-3.8323-3.4772-3.9657-0.1553-0.0343-0.3181-0.0343-0.5228-0.0343-1 0-2 0-2 1.5 0 0.4035 0.0724 0.8432 0.1587 1.3678 0.2346 1.4253 0.5723 3.4772-0.1587 7.1322-0.1065 0.5324-0.213 1.0478-0.3158 1.5456l-5e-4 0.0023-8e-4 0.0039-2e-4 0.0011c-0.8624 4.1732-1.4687 7.1076 0.3173 8.4471 1.568 1.176 3.136 1.1227 5.4269 1.0448 0.6311-0.0215 1.3171-0.0448 2.0731-0.0448 3.5 0 7-2 7-2s3.5-2 7.5-2 7.5 2.5 7.5 2.5 2 1 5.5 1 7-4.5 7-4.5 1.5 0 1.5 1-0.5 3-2.5 5c-0.292 0.2918-0.562 0.5836-0.827 0.8692-1.55 1.6718-2.902 3.1308-7.173 3.1308-2.5 0-4-0.75-5.5-1.5s-3-1.5-5.5-1.5c-2.9912 0-4.5509 1.2526-6.0706 2.4732-1.0206 0.8198-2.0233 1.625-3.4294 2.0268-1.6076 0.4593-2.8987 0.2857-4.2126 0.1091-1.5466-0.208-3.1246-0.4201-5.2874 0.3909-4 1.5-6.5 1-9 0s-5-3.5-5-6c0-1.1931 0.2277-2.1584 0.4659-3.1677 0.2608-1.1056 0.5341-2.264 0.5341-3.8323 0-1.5684-0.2733-2.8634-0.5341-4.0994-0.2382-1.1284-0.4659-2.2076-0.4659-3.4006v-4.5c0-2-1-2-1-2h-2l-1 2s-1 4 0 10-0.5 10-0.5 10-2 4-7 5-6.5 0.5-10.5-1c-2.7335-1.0251-4.066-1.8166-5.2741-2.5343-0.5597-0.3325-1.0927-0.6491-1.7259-0.9657-2-1-5.5-2-8-2-2.1353 0-3.541 0.7295-5.1519 1.5654-0.2752 0.1428-0.5563 0.2887-0.8481 0.4346-2 1-6.5 2-9.5 1.5s-4-1.5-5.5-3-2-3-2-4 0.5-2 0.5-2 1 0 1.5 1c0.07602 0.152 0.14049 0.3041 0.20569 0.4579l3e-5 1e-4c0.36366 0.8577 0.75049 1.7701 3.2943 3.042 3 1.5 6.5 1 9.5-1.5s5.5-3 8-3 6 0.5 9.5 2.5 8 3.5 9.5 3.5 3.5-0.5 4.5-2 1.5-3.5 1-6-0.5-3-0.5-5 1-6.5 1-6.5-2 3.5-4 4.5c-0.3246 0.1623-0.6754 0.3246-1.0356 0.4911-1.8591 0.8598-3.9644 1.8335-3.9644 3.5089 0 2 2 3 4 2 1 1-0.5 3-2 3.5s-3.5 0-3.5 0c-2.7324-1.7992-3.631-4.2374-2.5-7.5l4.5-7c1.3743-1.9413 1.4258-2.4981-0.5-2-3.8706 3.1725-6.2716 4.6073-11 6.5-4.4646 1.72-7.5311 2.0505-14 1.5-4.279-0.5754-6.5-1.5-9-4s-2.5-5.5-2-8.5 4-5.5 6-5.5c1.959 0 3.2761 0.6922 3.958 2.3919 0.1879 1.5095-0.0451 1.9427-0.958 2.1081-0.8004-2.19-2-2.5-3.5-2s-2 1.5-2.5 3-0.5 5 2.5 7c6.5 3.5 13.5 0.5 20.5-5.5 1.5-1.2857 3-3 4-5s1-2.75 1-4.875-2-8.125-2-8.125-2-4-2-12zm-17.03 25.423-0.0124-0.0311-2e-4 -0.0015 0.0126 0.0326zm0 0 0.0151 0.0388 0.0145 0.0382-0.0296-0.077zm62.03-13.423c0 4.4183-2.2386 8-5 8s-5-3.5817-5-8 2.2386-8 5-8 5 3.5817 5 8zm-29 8c2.7614 0 5-2.2386 5-5s-2.2386-5-5-5-5 2.2386-5 5 2.2386 5 5 5zm8-14v-0.5c-0.2125 0-0.3347 0-0.4433 0.0384-0.147 0.0519-0.2692 0.1741-0.5567 0.4616-0.5 0.5-1 1-4.5-1-0.2188-0.1459-0.427-0.2917-0.6307-0.4345l-1e-4 -1e-4c-1.1929-0.8359-2.2339-1.5654-4.3692-1.5654-1.5 0-3.5 1-4.5 2s-2.5 1-2.5 1 0 1 1.5 1c0.6547 0 1.2142-0.3811 1.8448-0.8105 0.814-0.5544 1.7464-1.1895 3.1552-1.1895 2.1353 0 3.541 0.7295 5.1519 1.5654l2e-4 1e-4c0.2751 0.1428 0.5562 0.2886 0.8479 0.4345 1.1875 0.5937 1.6699 0.8349 2.1798 0.9329 0.349 0.0671 0.7108 0.0671 1.3202 0.0671 1.5 0 1.5-2 1.5-2zm12-7.5s1 0.5 4-1.5 4-2 5.5-2 2.5 1 3.5 2c0.7133 0.7133 0.9178 0.9178 1.1579 0.9764 0.0965 0.0236 0.1988 0.0236 0.3421 0.0236 0.5 0 0.5-1 0.5-1s-1-0.5-2.5-2-2.5-2-4.5-2-4 2-4 2l-2 1.5s-0.5 0.5-2 0.5-3-0.5-3-0.5v0.5s0 0.5 1 1 2 0.5 2 0.5z" fill="currentColor" fill-rule="evenodd"></path><path d="m100 12c-4 4-7 6-7 6s3 6 3 14-3 15-3 15l-1 4 1 2s2.5-7 5-7 6 7 6 7 3.5-15 10-15 12 4 12 4-5-10-9-22-4-17-5-17.5-1-0.5-2-0.5-6 6-10 10z" fill="currentColor"></path><path d="m28 12c4 4 7 6 7 6s-3 6-3 14 3 15 3 15l1 4-1 2s-2.5-7-5-7-6 7-6 7-3.5-15-10-15-12 4-12 4 5-10 9-22 4-17 5-17.5 1-0.5 2-0.5 6 6 10 10z" fill="currentColor"></path></svg></span>
</a>
</div>
</div>
<div id="tab-pane-editor">
<div class="tab-editor-textfield tab-title">
<label for="tab-title" id="tab-title-label">Name</label>
<input id="tab-title" maxlength="50" onchange="save()" oninput="onTitleInput()">
</div>
<div class="tab-editor-textfield">
<label for="tab-icon" id="tab-icon-label">Icon</label>
<input id="tab-icon" maxlength="300" onchange="save()" oninput="onIconInput()">
<div class="tab-icon-dropdown">
<button class="tab-icon-dropdown-button" onclick="onIconDropdownClick()">
<span style="line-height:1em; vertical-align:middle;"><svg style="height:1em; width:1em" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#duotone-icons"/></svg></span>
</button>
<div class="tab-icon-dropdown--content" id="tab-icon-dropdown">
<button title="none"></button>
<button title="markdown"></button>
<button title="bones"></button>
<button title="steam-card"></button>
<button title="boost"></button>
<button title="fa-left"></button>
<button title="fa-right"></button>
<button title="fa-right-left"></button>
<button title="fa-code"></button>
<button title="fa-markup"></button>
<button title="fa-list"></button>
<button title="fa-checklist"></button>
<button title="fa-memo-pad"></button>
<button title="fa-note"></button>
<button title="fa-store"></button>
<button title="fa-cart-shopping"></button>
<button title="alpha-0"></button>
<button title="alpha-1"></button>
<button title="alpha-2"></button>
<button title="alpha-3"></button>
<button title="alpha-4"></button>
<button title="alpha-5"></button>
<button title="alpha-6"></button>
<button title="alpha-7"></button>
<button title="alpha-8"></button>
<button title="alpha-9"></button>
<button title="alpha-a"></button>
<button title="alpha-b"></button>
<button title="alpha-c"></button>
<button title="alpha-d"></button>
<button title="alpha-e"></button>
<button title="alpha-f"></button>
<button title="emoji-check"></button>
<button title="emoji-cross"></button>
<button title="emoji-question"></button>
<button title="emoji-exclamation"></button>
<button title="emoji-bangbang"></button>
<button title="emoji-interrobang"></button>
<button title="emoji-100"></button>
<button title="emoji-pushpin"></button>
<button title="emoji-light-bulb"></button>
<button title="emoji-heart"></button>
<button title="emoji-poo"></button>
<button title="emoji-rocket"></button>
<button title="emoji-happy"></button>
<button title="emoji-mad"></button>
<button title="emoji-devil"></button>
<button title="emoji-melting-face"></button>
<button title="emoji-eyes"></button>
<button title="emoji-thumbs-up"></button>
<button title="emoji-thumbs-down"></button>
<button title="emoji-horns"></button>
<button title="emoji-game-die"></button>
<button title="emoji-alien-monster"></button>
<button title="emoji-joystick"></button>
<button title="emoji-controller"></button>
<button title="emoji-search"></button>
<button title="emoji-alien"></button>
<button title="emoji-jack-o-lantern"></button>
<button title="emoji-skull"></button>
<button title="emoji-spider"></button>
<button title="emoji-christmas-tree"></button>
<button title="emoji-gift"></button>
<button title="emoji-party-popper"></button>
<button title="emoji-apple"></button>
<button title="emoji-cherries"></button>
<button title="emoji-grapes"></button>
<button title="emoji-lemon"></button>
<button title="emoji-strawberry"></button>
<button title="emoji-pretzel"></button>
<button title="emoji-hamburger"></button>
<button title="emoji-french-fries"></button>
<button title="emoji-pizza"></button>
<button title="emoji-taco"></button>
<button title="emoji-sun"></button>
<button title="emoji-moon"></button>
<button title="emoji-star"></button>
<button title="emoji-sparkles"></button>
<button title="emoji-trophy"></button>
<button title="emoji-fire"></button>
<button title="emoji-ruler"></button>
<button title="emoji-compass"></button>
<button title="emoji-clock"></button>
<button title="emoji-calendar"></button>
<button title="emoji-folder"></button>
<button title="emoji-memo"></button>
<button title="emoji-scroll"></button>
<button title="emoji-paperclip"></button>
<button title="emoji-locked"></button>
<button title="emoji-unlocked"></button>
<button title="emoji-floppy-disk"></button>
<button title="emoji-gem-stone"></button>
<button title="emoji-evil-eye"></button>
<button title="emoji-musical-notes"></button>
<button title="emoji-ticket"></button>
<button title="emoji-gear"></button>
</div>
</div>
</div>
<div class="tab-editor-textarea">
<div class="tab-label-row">
<label for="tab-note" id="tab-note-label">Note</label>
<div class="tab-editor-options">
<button class="tab-editor-option-button" data-enabled="0" id="option-line-wrap" onclick="onToggleLineWrapClick()" title="Line Wrap">
<span style="line-height:1em; vertical-align:middle;"><svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><use style="--fa-pc:currentColor;--fa-po:1;--fa-sc:currentColor;--fa-so:0.4;" xlink:href="#solid-turn-down-left"/></svg></span>
</button>
</div>
<div class="tab-chars-remaining-label">Characters remaining:&#x2007<span class="tab-chars-remaining">0</span></div>
</div>
<textarea id="tab-note" maxlength="1520" onchange="save()" oninput="onNoteInput()"></textarea>
</div>
</div>
<div id="tab-pane-preview"></div>
</div>
<div id="svg-use-src" style="display:none">
<svg viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><g id="duotone-caret-square-right"><path class="fa-secondary" d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zm-83.82 232L182.29 380.65c-8.22 7.16-22.29 2.09-22.29-8V139.4c0-10.14 14.06-15.21 22.29-8.05L316.18 248a10.38 10.38 0 0 1 0 16z" style="fill:var(--fa-sc); opacity:var(--fa-so)"/><path class="fa-primary" d="M316.18 264L182.29 380.65c-8.22 7.16-22.29 2.09-22.29-8V139.4c0-10.14 14.07-15.21 22.29-8.05L316.18 248a10.38 10.38 0 0 1 0 16z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
<svg viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><g id="duotone-caret-square-down"><path class="fa-secondary" d="M400 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V80a48 48 0 0 0-48-48zm-51.37 182.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" style="fill:var(--fa-sc); opacity:var(--fa-so)"/><path class="fa-primary" d="M348.63 214.31L232.06 348.16a10.38 10.38 0 0 1-16.12 0L99.37 214.31C92.17 206 97.28 192 107.43 192h233.14c10.15 0 15.26 14 8.06 22.31z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
<svg viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg"><g id="duotone-edit"><path class="fa-secondary" d="M564.6 60.2l-48.8-48.8a39.11 39.11 0 0 0-55.2 0l-35.4 35.4a9.78 9.78 0 0 0 0 13.8l90.2 90.2a9.78 9.78 0 0 0 13.8 0l35.4-35.4a39.11 39.11 0 0 0 0-55.2zM427.5 297.6l-40 40a12.3 12.3 0 0 0-3.5 8.5v101.8H64v-320h229.8a12.3 12.3 0 0 0 8.5-3.5l40-40a12 12 0 0 0-8.5-20.5H48a48 48 0 0 0-48 48v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V306.1a12 12 0 0 0-20.5-8.5z" style="fill:var(--fa-sc); opacity:var(--fa-so)"/><path class="fa-primary" d="M492.8 173.3a9.78 9.78 0 0 1 0 13.8L274.4 405.5l-92.8 10.3a19.45 19.45 0 0 1-21.5-21.5l10.3-92.8L388.8 83.1a9.78 9.78 0 0 1 13.8 0z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
<svg viewBox="0 0 448 512" xmlns="http://www.w3.org/2000/svg"><g id="duotone-save"><path class="fa-secondary" d="M288 352a64 64 0 1 1-64-64 64 64 0 0 1 64 64z" style="fill:var(--fa-sc); opacity:var(--fa-so)"/><path class="fa-primary" d="M433.94 129.94l-83.88-83.88A48 48 0 0 0 316.12 32H48A48 48 0 0 0 0 80v352a48 48 0 0 0 48 48h352a48 48 0 0 0 48-48V163.88a48 48 0 0 0-14.06-33.94zM224 416a64 64 0 1 1 64-64 64 64 0 0 1-64 64zm96-204a12 12 0 0 1-12 12H76a12 12 0 0 1-12-12V108a12 12 0 0 1 12-12h228.52a12 12 0 0 1 8.48 3.52l3.48 3.48a12 12 0 0 1 3.52 8.48z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g id="duotone-icons"><path class="fa-secondary" d="M137.86 22.44L128 32.58l-9.85-10.14C93.05-3.5 52.25-7.7 24.86 15.64c-31.41 26.78-33 74.85-5 103.88l96.75 99.83a15.68 15.68 0 0 0 22.65 0l96.75-99.83c28.14-29 26.5-77.1-4.91-103.88C203.75-7.7 163-3.5 137.86 22.44zM499.4 352.1h-60.58l22.36-50.75c2.1-6.65-3.94-13.21-12.18-13.21h-75.6c-6.3 0-11.65 3.9-12.49 9.1l-16.8 106.93c-1 6.3 4.88 11.89 12.49 11.89h62.32l-24.2 83c-1.89 6.65 4.2 12.9 12.23 12.9a13.26 13.26 0 0 0 10.92-5.25l92.4-138.91c4.88-6.91-1.16-15.7-10.87-15.7z" style="fill:var(--fa-sc); opacity:var(--fa-so)"/><path class="fa-primary" d="M260.57 319.84h-48l-7.08-14.24a27.39 27.39 0 0 0-25.66-17.78h-71.71a27.39 27.39 0 0 0-25.66 17.78l-7 14.24h-48A27.45 27.45 0 0 0 0 347.3v137.25A27.45 27.45 0 0 0 27.43 512h233.14A27.45 27.45 0 0 0 288 484.55V347.3a27.45 27.45 0 0 0-27.43-27.46zM144 468a52 52 0 1 1 52-52 52 52 0 0 1-52 52zM478.08.33L329.51 23.17C314.87 25.42 304 38.92 304 54.83V161.6a83.25 83.25 0 0 0-16-1.7c-35.35 0-64 21.48-64 48s28.65 48 64 48c35.2 0 63.73-21.32 64-47.66V99.66l112-17.22v47.18a83.25 83.25 0 0 0-16-1.7c-35.35 0-64 21.48-64 48s28.65 48 64 48c35.2 0 63.73-21.32 64-47.66V32c0-19.48-16-34.42-33.92-31.67z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><g id="solid-turn-down-left"><path class="fa-primary" d="M5.7 274.3L143.7 136.3c5.3-5.3 12.5-8.3 20-8.3c15.6 0 28.3 12.7 28.3 28.3l0 83.7 208 0c8.8 0 16-7.2 16-16l0-160c0-17.7 14.3-32 32-32l32 0c17.7 0 32 14.3 32 32l0 160c0 61.9-50.1 112-112 112l-208 0 0 83.7c0 15.6-12.7 28.3-28.3 28.3c-7.5 0-14.7-3-20-8.3L5.7 301.6C2 298 0 293.1 0 288s2-10 5.7-13.7z" style="fill:var(--fa-pc); opacity:var(--fa-po)"/></g></svg>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.0/showdown.min.js"></script>
<script>
const preview = document.getElementById('tab-pane-preview');
const editor = document.getElementById('tab-pane-editor');
const tabEdit = document.getElementById('tab-mode');
const tabState = document.getElementById('tab-state');
const tabTitle = document.getElementById('tab-title');
const tabIcon = document.getElementById('tab-icon');
const tabIconPanel = document.querySelector('.tab-icon-dropdown--content');
const tabOptionLineWrap = document.getElementById('option-line-wrap');
const tabNote = document.getElementById('tab-note');
const tabNoteRemaining = tabNote.parentNode.querySelector('.tab-chars-remaining');
const serializationSeparator = "\u001f"; // US :: Unit Separator
const tokenData = 'd';
const uriUpToQueryLength = getUriUpToQueryLength(tokenData);
const maxSerializedLength = calculateMaxSerializedLength(uriUpToQueryLength);
const tabNoteMaxLength = maxSerializedLength;
// LOG("tokenData: " + tokenData);
// LOG("uriUpToQueryLength: " + uriUpToQueryLength);
// LOG("maxSerializedLength: " + maxSerializedLength);
// LOG("tabNoteMaxLength: " + tabNoteMaxLength);
const booleanToDigit = (bool) => bool ? '1' : '0';
const digitToBoolean = (digit) => digit === '1';
const removeUnitSeparator = (text) => text.replace(/\u001f/g, '');
const converter = new showdown.Converter();
converter.setOption('tables', 'true');
converter.setOption('tasklists', 'true');
converter.setOption('emoji', 'true');
converter.setOption('underline', 'true');
converter.setOption('strikethrough', 'true');
converter.setOption('literalMidWordUnderscores', 'true');
converter.setOption('moreStyling', 'true');
converter.setOption('parseImgDimensions', 'true');
converter.setOption('omitExtraWLInCodeBlocks', 'true');
function calculateMaxSerializedLength(uriUpToQueryLength) {
const maxUrlLength = 2048;
const remainingLength = maxUrlLength - uriUpToQueryLength;
const maxGroups = Math.floor(remainingLength / 4) - 1; // bump down to allow for `==` encode expansion
const maxSerializedLength = maxGroups * 3;
return maxSerializedLength;
}
function deserialize(data) {
let parts = data.split(serializationSeparator);
let mode = digitToBoolean(parts[0]);
let lineWrap = digitToBoolean(parts[1]);
let title = parts[2];
let icon = parts[3];
let note = parts[4];
return { mode, lineWrap, title, icon, note };
}
function editDisabled() {
tabEdit.dataset.mode = "0";
markDirty();
save();
}
function editEnabled() {
tabEdit.dataset.mode = "1";
markDirty();
save();
}
// Function to get the computed style value as a number
function getComputedStyleValue(element, property) {
const computedStyle = window.getComputedStyle(element);
const value = computedStyle.getPropertyValue(property);
return parseFloat(value); // Convert to numeric value
}
function getUriUpToQueryLength(queryToken) {
const protocol = window.location.protocol; // e.g., "https:"
const domain = window.location.host; // e.g., "example.com", "127.0.0.1:12345", or "localhost:12345"
const path = window.location.pathname; // e.g., "/tab/"
// Construct the base URI up to the query assignment, and return the length
const baseUri = `${protocol}//${domain}${path}?${queryToken}=`;
// LOG(protocol.length + ": " + protocol);
// LOG(domain.length + ": " + domain);
// LOG(path.length + ": " + path);
// LOG(baseUri.length + ": " + baseUri);
return baseUri.length;
}
function initDropdownIcon(el) {
let iconTitle = el.title;
if (iconTitle === undefined) {
console.warn('Dropdown icon has bad or missing title attribute');
return;
}
el.onclick = function () {
setIcon(iconTitle)
};
let img = document.createElement('img');
img.src = '/tab/icon/' + iconTitle + '/24.png';
el.appendChild(img);
}
function initDropdownIcons() {
let buttons = document.getElementById('tab-icon-dropdown').getElementsByTagName('button');
for (let i = 0, n = buttons.length; i < n; i++) {
initDropdownIcon(buttons[i]);
}
}
function isDirty() {
return tabState.dataset.dirty === 'true';
}
function load() {
let url = new URL(window.location.href);
let encodedData = url.searchParams.get(tokenData) ?? "";
if (encodedData) {
let base64Data = decodeURIComponent(encodedData);
let serializedData = atob(base64Data);
let { mode, lineWrap, title, icon, note } = deserialize(serializedData);
let isEditEnabled = mode;
if (isEditEnabled) {
editor.classList.add('tab-editor--enabled');
tabState.classList.add('tab-editor--enabled');
}
let optionEnabled = lineWrap;
setOption(tabOptionLineWrap, optionEnabled, tabNote, 'tab-editor-option-wrap');
tabEdit.dataset.mode = isEditEnabled ? "1" : "0";
tabTitle.value = title;
tabIcon.value = icon;
tabNote.value = note;
} else {
let mode = url.searchParams.get('e') ?? "0";
let icon = url.searchParams.get('i') ?? "";
let note = url.searchParams.get('n') ?? "";
let title = url.searchParams.get('t') ?? "";
let isEditEnabled = mode === "1";
if (isEditEnabled) {
editor.classList.add('tab-editor--enabled');
tabState.classList.add('tab-editor--enabled');
}
let optionEnabled = (url.searchParams.get('w') ?? "1") === "1";
setOption(tabOptionLineWrap, optionEnabled, tabNote, 'tab-editor-option-wrap');
tabEdit.dataset.mode = isEditEnabled ? "1" : "0";
tabTitle.value = atob(decodeURIComponent(title));
tabIcon.value = atob(decodeURIComponent(icon));
tabNote.value = atob(decodeURIComponent(note));
}
updateTitle(true);
updateIcon(true);
onNoteInput(true);
markSaved();
}
function LOG(obj) {
console.log(obj);
}
function markDirty() {
tabState.dataset.dirty = 'true';
}
function markSaved() {
tabState.dataset.dirty = 'false';
}
function onIconDropdownClick() {
document.getElementById("tab-icon-dropdown").classList.toggle("tab-icon-dropdown--show");
resizeDropdown();
}
function onIconInput(forceUpdate = false) {
updateIcon(forceUpdate);
updateRemaining(tabNoteRemaining);
}
function onNoteInput(forceUpdate = false) {
updatePreview(forceUpdate);
updateRemaining(tabNoteRemaining);
}
function onTitleInput(forceUpdate = false) {
updateRemaining(tabNoteRemaining);
updateTitle(forceUpdate);
}
function onToggleLineWrapClick() {
toggleOption(tabOptionLineWrap, tabNote, 'tab-editor-option-wrap');
markDirty();
save();
}
function replacePatterns(text) {
const vineRegex = /]\((@vine:\s*(\S*)\s*)\)/igm;
text = text.replaceAll(vineRegex, '](https://www.amazon.com/vine/vine-items?search=$2)');
const cardsRegex = /^(\|\{CARDS}\|)$/igm;
text = text.replaceAll(cardsRegex, '🍒 1:1 [H] - [W] -\n\n1:1 trades within this same set. Thanks!\n\n|Card|Need|Bots|\n|---|:--:|---|');
return text;
}
function resizeDropdown() {
const contentElement = document.getElementById('tab-panes');
const dropdownElement = document.getElementById('tab-icon-dropdown');
const buttonElement = dropdownElement.parentElement;
const previewElement = document.getElementById('tab-pane-preview');
const contentRect = contentElement.getBoundingClientRect();
const buttonRect = buttonElement.getBoundingClientRect();
const dropdownRect = dropdownElement.getBoundingClientRect();
const previewRect = previewElement.getBoundingClientRect();
// Calculate and set the maximum height for dropdownElement, so that the bottom of
// the element is less than or equal to the bottom of previewElement.
const maxHeight = previewRect.bottom - dropdownRect.top;
dropdownElement.style.maxHeight = `${Math.max(maxHeight, 0)}px`;
// if (maxHeight > 0) {
// dropdownElement.style.maxHeight = `${maxHeight}px`;
// } else {
// dropdownElement.style.maxHeight = '0px'; // Ensure it's not negative
// }
// Get the initial maxWidth value only once
if (dropdownInitialMaxWidth === undefined) {
dropdownInitialMaxWidth = getComputedStyleValue(dropdownElement, 'max-width');
}
// Calculate and set the maximum width for dropdownElement, so that the right side of
// the element is less than or equal to the right side of previewElement.
const maxWidth = previewRect.right - dropdownRect.left;
dropdownElement.style.maxWidth = `${Math.min(Math.max(maxWidth, 0), dropdownInitialMaxWidth)}px`;
// LOG('----------');
// LOG('window.innerWidth: ' + window.innerWidth);
// LOG('contentRect: ' + contentRect.toString());
// LOG('buttonRect: ' + buttonRect.toString());
// LOG('dropdownRect: ' + dropdownRect.toString());
// LOG('previewRect: ' + previewRect.toString());
}
function save() {
if (!isDirty()) {
return;
}
let serializedData = serialize(
maxSerializedLength,
digitToBoolean(tabEdit.dataset.mode),
digitToBoolean(tabOptionLineWrap.dataset.enabled),
tabTitle.value,
tabIcon.value,
tabNote.value
);
let base64Data = btoa(serializedData);
let uriEncodedData = encodeURIComponent(base64Data);
let url = new URL(window.location.href);
url.search = '';
setSearchParam(url, tokenData, uriEncodedData, "1");
window.location.href = url.href;
markSaved();
}
function serialize(maxLength, mode, lineWrap, title, icon, note) {
let data = [
booleanToDigit(mode),
booleanToDigit(lineWrap),
removeUnitSeparator(title),
removeUnitSeparator(icon),
removeUnitSeparator(note)
].join(serializationSeparator);
let truncated = data.substring(0, maxLength);
if (truncated.length != data.length)
{
console.log(`The serialized data was truncated to allow the URI to fit within the 2048-character limit. ${data.length - truncated.length} were lost.`);
}
return truncated;
}
function setIcon(iconTitle = '') {
tabIcon.value = 'icon:' + iconTitle;
onIconInput();
save();
}
function setOption(option, enabled, target, className) {
option.dataset.enabled = enabled ? "1" : "0";
if (enabled) {
target.classList.add(className);
} else {
target.classList.remove(className);
}
}
function setSearchParam(url, key, value, defaultValue) {
url.searchParams.delete(key);
if (value !== defaultValue) {
url.searchParams.set(key, value);
}
}
function toggleOption(option, target, className) {
let enabled = option.dataset.enabled === "1";
setOption(option, !enabled, target, className);
}
function updateIcon(forceUpdate = false) {
let icon = tabIcon.value.trim();
const appleTouchIcons = document.querySelectorAll("link[rel='apple-touch-icon']");
const pngIcons = document.querySelectorAll("link[rel='icon'][type='image/png']");
const msTileImage = document.querySelector("meta[name='msapplication-TileImage']");
const getIconPath = (size) => {
if (icon.startsWith('icon:')) {
const iconName = icon.replace('icon:', '').replace('/', '');
return `icon/${iconName}/${size}.png`;
}
return icon.length > 0 ? icon : `/favicon/${size}.png`;
};
appleTouchIcons.forEach(iconLink => {
const size = iconLink.getAttribute('sizes').split('x')[0];
iconLink.href = getIconPath(size);
});
pngIcons.forEach(iconLink => {
const size = iconLink.getAttribute('sizes').split('x')[0];
iconLink.href = getIconPath(size);
});
msTileImage.setAttribute('content', getIconPath(144));
if (!forceUpdate) {
markDirty();
}
}
function updatePreview(forceUpdate = false) {
let text = replacePatterns(tabNote.value);
let html = converter.makeHtml(text);
if (preview.innerHTML !== html) {
preview.innerHTML = html;
if (!forceUpdate) {
markDirty();
}
}
}
function updateRemaining(el) {
// There are five fields consisting of three text (name, icon, and note) and two boolean (mode and lineWrap).
// The fields are separated by a single control code character. Therefore, the remaining characters should be
// decreased by the length of the text fields, 2 for the bool, and 4 for the separators.
const value = tabNoteMaxLength - tabNote.value.length - tabTitle.value.length - tabIcon.value.length - 2 - 4;
el.innerHTML = (value >= 0) ? value
: `<span class="tab-chars-remaining-warning" title="Too many characters - some won't be saved">${value}</span>`;
}
function updateTitle(forceUpdate = false) {
const ZeroWidthSpace = '\u200B';
let title = tabTitle.value.trim();
document.title = title.length > 0 ? title : ZeroWidthSpace;
if (!forceUpdate) {
markDirty();
}
}
// Close the dropdown if the user clicks outside of it
window.onclick = function (event) {
if (!tabIconPanel.classList.contains('tab-icon-dropdown--show')) {
return;
}
let el = event.target.closest('.tab-icon-dropdown-button');
if (el === null && event.target !== tabIconPanel) {
tabIconPanel.classList.remove('tab-icon-dropdown--show');
}
}
DOMRect.prototype.toString = function() {
return `DOMRect(${parseFloat(this.x.toFixed(2)).toString()}, ${parseFloat(this.y.toFixed(2)).toString()}, ${parseFloat(this.width.toFixed(2)).toString()}, ${parseFloat(this.height.toFixed(2)).toString()}) (${parseFloat(this.top.toFixed(2)).toString()}, ${parseFloat(this.right.toFixed(2)).toString()}, ${parseFloat(this.bottom.toFixed(2)).toString()}, ${parseFloat(this.left.toFixed(2)).toString()})`;
};
let dropdownInitialMaxWidth;
window.addEventListener('resize', resizeDropdown);
load();
initDropdownIcons();
</script></description><dc:creator>Michael Ryan</dc:creator><category>app</category><category>browser</category><category>customization</category><category>icons</category><category>markdown</category><category>productivity</category><category>tabs</category><category>general</category></item><item><title>Commenting with Giscus</title><link>https://somethingstrange.com/posts/commenting-with-giscus/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><atom:modified>Sat, 15 Feb 2025 23:31:48 -0800</atom:modified><guid>https://somethingstrange.com/posts/commenting-with-giscus/</guid><description><p>For a while now, this website has been powered by <a href="https://gohugo.io/">Hugo</a>, a fantastic static site generator that ensures the site content is compiled into static files, making it swift and efficient. As a static website, there is no backend server running scripts, which means fewer points of failure and faster load times.</p>
<p>I&rsquo;ve been having fun experimenting with CSS and JavaScript to add some dynamic flair, interactive elements, and sleek transitions to bring a modern touch to the site. At its core, however, it&rsquo;s all still a beautifully simple static site.</p></description><dc:creator>Michael Ryan</dc:creator><category>giscus</category><category>github</category><category>hugo</category><category>webdev</category></item><item><title>Closing Duplicate Tabs in Directory Opus</title><link>https://somethingstrange.com/posts/closing-duplicate-tabs-in-dopus/</link><pubDate>Sun, 29 Dec 2024 00:00:00 +0000</pubDate><atom:modified>Sat, 15 Feb 2025 23:31:48 -0800</atom:modified><guid>https://somethingstrange.com/posts/closing-duplicate-tabs-in-dopus/</guid><description><p>One of the standout features of <em>Directory Opus</em> is its capability to manage multiple listers and a multitude of open tabs. However, this flexibility can quickly lead to a cluttered workspace if redundant tabs aren&rsquo;t regularly closed.</p>
<p>The script helps simplify tab management by automatically identifying and closing duplicate tabs, ensuring that only unique and necessary tabs remain open. This allows users to maintain a cleaner, more efficient workspace and reduces the time spent manually sorting through tabs.</p></description><dc:creator>Michael Ryan</dc:creator><category>directory opus</category><category>general</category></item><item><title>Sorting Tabs in Directory Opus</title><link>https://somethingstrange.com/posts/sorting-tabs-in-dopus/</link><pubDate>Sat, 28 Dec 2024 00:00:00 +0000</pubDate><atom:modified>Fri, 13 Jun 2025 12:42:26 -0700</atom:modified><guid>https://somethingstrange.com/posts/sorting-tabs-in-dopus/</guid><description><p>One of the best features of <em>Directory Opus</em> is the ability to have multiple listers with numerous open tabs. However, if you&rsquo;re not diligent in closing unnecessary tabs, you&rsquo;ll quickly end up with a cluttered mix of relevant and irrelevant tabs.</p>
<p>Managing a multitude of tabs can be daunting, especially when you need to keep them organized for efficient workflow. The script in this article aims to streamline this process by automatically sorting your tabs based on their paths. It distinguishes between locked and unlocked tabs, ensuring your most important tabs remain accessible while others are neatly arranged.</p></description><dc:creator>Michael Ryan</dc:creator><category>directory opus</category><category>general</category></item><item><title>Using <use> with Font Awesome SVGs</title><link>https://somethingstrange.com/posts/using-use-with-fontawesome-svgs/</link><pubDate>Sat, 14 Dec 2024 00:00:00 +0000</pubDate><atom:modified>Thu, 26 Jun 2025 11:38:45 -0700</atom:modified><guid>https://somethingstrange.com/posts/using-use-with-fontawesome-svgs/</guid><description><p>Throughout this site, I use SVG icons by inlining them directly into the page, ensuring immediate availability without unnecessary network calls. Basic inlining would embed data for each SVG separately, even if the same icon is used multiple times, quickly bloating the page with duplicate data. But there&rsquo;s a solution!</p>
<p>SVGs support the <code>&lt;use&gt;</code> element, which references and duplicates content from another SVG document. This process effectively clones nodes into a hidden DOM and pastes them where the <code>&lt;use&gt;</code> element is, similar to how <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template">template elements</a> are cloned.</p></description><dc:creator>Michael Ryan</dc:creator><category>font awesome</category><category>hugo</category><category>icons</category><category>webdev</category></item><item><title>GameView Sizes in Unity</title><link>https://somethingstrange.com/posts/game-view-sizes-in-unity/</link><pubDate>Thu, 23 Mar 2023 00:00:00 +0000</pubDate><atom:modified>Sat, 15 Feb 2025 23:31:48 -0800</atom:modified><guid>https://somethingstrange.com/posts/game-view-sizes-in-unity/</guid><description><style>
body {
overflow-x: hidden;
width: 100%;
}
.now-you-know .sc-picture__composite {
max-width: 320px;
}
.now-you-know .sc-picture__caption {
text-align: center;
font-family: "Army Rangers", sans-serif;
font-size: 1.2em;
font-style: normal;
position: relative;
left: 1em;
text-shadow: 1px 1px black;
}
.now-you-know .sc-picture__title {
display: block;
position: relative;
left: -0.5em;
font-size: 2.5em;
line-height: 1em;
background: linear-gradient(177deg, #b22234 30%, white 45%, white 55%, #3c3b6e 70%);
background-size: 100%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-text-stroke: 1px black;
text-shadow: initial;
}
.object-properties {
margin: 1rem 2rem 2rem;
/*padding: 1em 0 0 1em;*/
/*box-shadow: inset 0 0 16px rgba(0,0,0,0.25);*/
}
.object-properties table {
border-collapse: collapse;
border-spacing: 0;
}
.object-properties thead {
display: none;
}
.object-properties td {
vertical-align: top;
padding: 0.375rem 0.25rem;
}
.object-properties tr:not(:last-child) td {
padding-bottom: 1rem;
}
.object-properties td:not(:last-child) {
padding-right: 1rem;
}
.object-properties td:first-child {
border-radius: 0.375rem 0 0 0.375rem;
}
.object-properties td:last-child {
border-radius: 0 0.375rem 0.375rem 0;
width: 100%;
}
.object-properties tr:nth-child(even) {
background-color: blue;
background-color: hsl(var(--content-box-background-color-h), var(--content-box-background-color-s), calc(var(--content-box-background-color-l) - 3%));
}
.object-properties .hanging-indent {
padding-left: 2em ;
text-indent: -2em ;
}
</style>
<div style="background-color: var(--text-color); height: 100px; width: 100px;"></div>
The Unity Game view toolbar has a dropdown that allows you to select various resolutions and aspect ratios presets for testing how your game will look on different monitors. While custom presets may be added to that dropdown menu by clicking the <span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height:1em; width:1em"><use xlink:href="#solid-circle-plus" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> button at the bottom, there's no obvious way of removing items from the list once they're added.
<figure class="sc-picture__figure">
<picture>
<source type="image/avif" srcset="gameview-sizes-menu.avif">
<source type="image/webp" srcset="gameview-sizes-menu.webp">
<source type="image/jpeg" srcset="gameview-sizes-menu.jpg">
<source type="image/png" srcset="gameview-sizes-menu.png">
<img class="sc-picture__image" src="https://somethingstrange.com/gameview-sizes-menu.png" alt="gameview aspect ratio and screen size menu" decoding="auto" loading="eager">
</picture>
</figure>
<div style="display:flex;gap:2rem;flex-wrap:wrap;">
<div style="width:60%;min-width:300px;flex-grow:1;">
</div>
<div style="width:35%;min-width:300px;flex-grow:1;align-self:center;">
</div>
</div>
<p>Fortunately, there&rsquo;s a configuration file that contains all custom menu entries, and it&rsquo;s a standard Unity YAML data file that can be modified in any plain text editor. The file is called <span class="sc-path">GameViewSizes.asset</span>, and it&rsquo;s located under your user profile.</p></description><dc:creator>Michael Ryan</dc:creator><media:content url="https://somethingstrange.com/icon.jpg" medium="image"><media:title type="html">featured image</media:title></media:content><category>unity</category><category>gamedev</category></item><item><title>Natural Sorting of SemVer Strings in Hugo</title><link>https://somethingstrange.com/posts/natural-sorting-of-semver-strings-in-hugo/</link><pubDate>Thu, 13 Oct 2022 13:25:41 +0700</pubDate><atom:modified>Sat, 15 Feb 2025 23:31:48 -0800</atom:modified><guid>https://somethingstrange.com/posts/natural-sorting-of-semver-strings-in-hugo/</guid><description><p>Earlier today, I saw a <a href="https://discourse.gohugo.io/t/sorting-semantic-version-numbers/40838">post</a> on Hugo&rsquo;s Discourse site where someone was asking for a way to sort version numbers with a <a href="https://en.wikipedia.org/wiki/Natural_sort_order">natural sort order</a> where multi-digit numbers are treated atomically.</p>
<p>The <a href="https://discourse.gohugo.io/t/sorting-semantic-version-numbers/40838/4">accepted solution</a> seemed somewhat complicated with the way the versions were split into separate version components (i.e., Major, Minor, Patch, PreRelease), sorted into nested maps, and then merged together again afterward.</p>
<p>I believe the solution I came up with is a bit more straightforward, and it should be able to sort all SemVer strings. Also, since it doesn&rsquo;t assume the <code>MAJOR.MINOR.PATCH</code> format, it can also handle version strings with pre-release suffixes, such as &ldquo;beta&rdquo; and &ldquo;rc&rdquo;.</p></description><dc:creator>Michael Ryan</dc:creator><category>hugo</category><category>webdev</category></item><item><title>Hugo with Font Awesome</title><link>https://somethingstrange.com/posts/hugo-with-fontawesome/</link><pubDate>Thu, 17 Mar 2022 15:01:43 -0700</pubDate><atom:modified>Thu, 26 Jun 2025 11:38:45 -0700</atom:modified><guid>https://somethingstrange.com/posts/hugo-with-fontawesome/</guid><description><p><a href="https://fontawesome.com/">Font Awesome</a> is an icon font widely used across various websites, applications, and projects. I&rsquo;ve been utilizing it for years to develop editor tools for my Unity projects. It&rsquo;s truly awesome.</p>
<p>One of the first decisions I made as I started building this site with <a href="https://gohugo.io/">Hugo</a>, a popular open-source static site generator, was to integrate Font Awesome to handle my icon needs. I specifically wanted to use SVGs due to their numerous advantages over traditional font icons. Many <a href="https://cloudfour.com/thinks/seriously-dont-use-icon-fonts/">articles</a> detail the <a href="https://www.lambdatest.com/blog/its-2019-lets-end-the-debate-on-icon-fonts-vs-svg-icons/">advantages</a> of SVG icons <a href="https://www.irigoyen.dev/blog/2021/02/17/stop-using-icon-fonts/">over font icons</a>, so I won’t repeat those here.</p></description><dc:creator>Michael Ryan</dc:creator><category>font awesome</category><category>hugo</category><category>icons</category><category>webdev</category></item><item><title>Command-Line Syntax Key</title><link>https://somethingstrange.com/posts/command-line-syntax-key/</link><pubDate>Wed, 16 Mar 2022 01:10:00 -0800</pubDate><atom:modified>Fri, 13 Dec 2024 15:27:26 -0800</atom:modified><guid>https://somethingstrange.com/posts/command-line-syntax-key/</guid><description><p>The following table describes the notation used to indicate command-line syntax.</p>
<table>
<thead>
<tr>
<th>Notation</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Loose Text</td>
<td><em>Required</em> text that must be typed as shown.</td>
</tr>
<tr>
<td><span class="nowrap"><code>&lt;</code> … <code>&gt;</code></span></td>
<td>Placeholder for a <em>required</em> value.</td>
</tr>
<tr>
<td><span class="nowrap"><code>[</code> … <code>]</code></span></td>
<td>Set of <em>optional</em> items.</td>
</tr>
<tr>
<td><span class="nowrap"><code>{</code> … <code>}</code></span></td>
<td>Set of <em>required</em> items. You must choose one.</td>
</tr>
<tr>
<td><code>|</code> (pipe)</td>
<td>Pipe separator for mutually exclusive items.</td>
</tr>
<tr>
<td><span class=nowrap><code>...</code> (ellipsis)</span></td>
<td>Items that can be repeated and used multiple times.</td>
</tr>
</tbody>
</table></description><dc:creator>Michael Ryan</dc:creator><category>terminal</category><category>gamedev</category><category>webdev</category></item><item><title>Verdaccio, Synology, and Unity. Oh My!</title><link>https://somethingstrange.com/posts/verdaccio-synology-and-unity/</link><pubDate>Tue, 01 Mar 2022 13:30:03 -0800</pubDate><atom:modified>Fri, 13 Dec 2024 17:01:09 -0800</atom:modified><guid>https://somethingstrange.com/posts/verdaccio-synology-and-unity/</guid><description>Setting up a private Verdaccio package registry is generally quite easy, however it gets a bit more complicated when using a docker container on a Synology NAS.</description><dc:creator>Michael Ryan</dc:creator><category>synology</category><category>verdaccio</category><category>docker</category><category>unity</category><category>npm</category><category>gamedev</category></item><item><title>A Convenient Caboodle of Unicode Characters</title><link>https://somethingstrange.com/posts/a-convenient-caboodle-of-unicode-characters/</link><pubDate>Tue, 08 Feb 2022 22:55:31 -0800</pubDate><atom:modified>Wed, 28 May 2025 12:54:08 -0700</atom:modified><guid>https://somethingstrange.com/posts/a-convenient-caboodle-of-unicode-characters/</guid><description>Quick access to some useful Unicode characters.</description><dc:creator>Michael Ryan</dc:creator><category>unicode</category><category>gamedev</category><category>webdev</category></item><item><title>Photoshop Scripting</title><link>https://somethingstrange.com/posts/photoshop-scripting/</link><pubDate>Mon, 01 Nov 2021 16:53:42 -0800</pubDate><atom:modified>Fri, 13 Dec 2024 15:27:26 -0800</atom:modified><guid>https://somethingstrange.com/posts/photoshop-scripting/</guid><description><h2 id="the-scriptinglistener-plug-in">The ScriptingListener plug-in</h2>
<p>The ScriptingListener plug-in can record JavaScript to a log file for any operation which is actionable.</p>
<p>Install the ScriptingListener plug-in:</p>
<ol>
<li>
<p>Quit Photoshop.</p>
</li>
<li>
<p>Download the ScriptingListener plug-in package. This package contains the ScriptingListener plug-in in the &ldquo;Utilities&rdquo; folder, scripting documentation, and sample scripts.</p>
<ul>
<li><span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" style="height:1em; width:1em"><use xlink:href="#brands-windows" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> <strong>Windows</strong>:<br />
<span style="display:inline-block; width:1em;"></span><span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height:1em; width:1em"><use xlink:href="#solid-download" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> <a href="https://download.adobe.com/pub/adobe/photoshop/win/13.x/Win_Scripting_Plug-In.zip">Scripting Listener Plug-in for Windows</a></li>
<li><span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" style="height:1em; width:1em"><use xlink:href="#brands-apple" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> <strong>macOS</strong>:<br />
<span style="display:inline-block; width:1em;"></span><span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height:1em; width:1em"><use xlink:href="#solid-download" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> <a href="https://helpx.adobe.com/content/dam/help/en/photoshop/kb/downloadable-plugins-and-content/Scripting_Plug_In_Release.zip">Scripting Listener Plug-in for macOS</a> (Photoshop 2020 and later)<br />
<span style="display:inline-block; width:1em;"></span><span style="line-height:1em; vertical-align:middle;"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="height:1em; width:1em"><use xlink:href="#solid-download" style="--fa-pc:currentColor;--fa-po:1;"/></svg></span> <a href="https://download.adobe.com/pub/adobe/photoshop/mac/13.x/Scripting_Plug_In_Release.dmg">Scripting Listener Plug-in for macOS</a> (Photoshop 2019 and earlier)</li>
</ul>
</li>
<li>
<p>After you download the ScriptingListener plug-in package file above, double-click it to decompress it. If asked, extract all the files.</p></description><dc:creator>Michael Ryan</dc:creator><category>photoshop</category><category>js</category></item><item><title>An Indent Shortcode for Hugo</title><link>https://somethingstrange.com/posts/hugo-indent-shortcode/</link><pubDate>Wed, 27 Oct 2021 00:00:00 +0000</pubDate><atom:modified>Fri, 13 Dec 2024 17:01:09 -0800</atom:modified><guid>https://somethingstrange.com/posts/hugo-indent-shortcode/</guid><description><p>Indenting markdown usually isn&rsquo;t difficult, however there could be a few gotchas. Skip down to the end for a quick little Hugo <a href="https://somethingstrange.com/posts/hugo-indent-shortcode/#the-shortcode">shortcode</a> for inserting indents in markdown.</p>
<h3 id="the-problem">The Problem</h3>
<p>Earlier today, I wanted to indent a markdown page element that included a link, but for some reason, the HTML that wrapped the markdown link was breaking the things.</p>
<p>Search the web for how to &ldquo;indent without adding a bullet or number in markdown&rdquo; and you&rsquo;ll likely come across multiple suggestions that rely on a mix of HTML tags and CSS styles.</p></description><dc:creator>Michael Ryan</dc:creator><media:content url="https://somethingstrange.com/images/posts/windows-terminal.png" medium="image"><media:title type="html">featured image</media:title></media:content><category>markdown</category><category>Hugo</category><category>shortcode</category><category>webdev</category></item><item><title>The Markdown Grimoire</title><link>https://somethingstrange.com/posts/the-markdown-grimoire/</link><pubDate>Fri, 31 Oct 0730 00:00:00 +0000</pubDate><atom:modified>Mon, 30 Jun 2025 11:59:19 -0700</atom:modified><guid>https://somethingstrange.com/posts/the-markdown-grimoire/</guid><description><p><em>Transcribed from the crumbling vellum discovered beneath the shifting sands of Rub&rsquo; al Khali, 730 CE</em></p>
<div class="sidebar-scribble">
<span>The symbols must never be spoken aloud.</span><br>
<span>They are not merely syntax — they are summoning.</span>
</div>
<p>Bound in a leather not quite animal, stained with the ink of bygone intellects, this manuscript was unearthed from the shattered ruins of a ziggurat whose shape defied Euclidean geometry. It is said that Abdul Alhazred, driven mad by revelations glimpsed beyond the veil of time, encoded forbidden truths in what modern scholars feebly call “Markdown.”</p></description><dc:creator>Abdul Alhazred</dc:creator><category>markdown</category><category>webdev</category></item></channel></rss>