-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.json
1 lines (1 loc) · 118 KB
/
index.json
1
[{"content":"For a while now, this website has been powered by Hugo, 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.\nI\u0026rsquo;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\u0026rsquo;s all still a beautifully simple static site.\nOne thing it\u0026rsquo;s lacked was a way for visitors to provide feedback and suggestions. So, I\u0026rsquo;m happily ringing in the New Year by integrating Giscus, an elegant and lightweight commenting system powered by GitHub Discussions. By leveraging Giscus, the site now enables readers to leave comments and suggestions right at the bottom of every article. This addition transforms the site\u0026rsquo;s static setup into an interactive platform where your voices can be heard.\nGiscus uses GitHub for authorization, providing a seamless and secure way for users to engage. While this might be a small hurdle for those without a GitHub account, I believe most of our readers who are keen on the topics discussed here are already part of the GitHub community.\nSo, feel free to leave your thoughts, feedback, and suggestions. Your input is invaluable, and I\u0026rsquo;m excited to hear from you. Happy commenting!\n","description":"Uh-oh, now I've done it! I've integrated Giscus for article comments. Dive in and share your thoughts—can't wait to hear what you have to say!","id":0,"section":"posts","tags":["giscus","github","hugo"],"title":"Commenting with Giscus","uri":"http://somethingstrange.com/posts/commenting-with-giscus/"},{"content":" Edited Saved Name 0 Icon 0 Note Characters remaining:\u0026#x20070 ","description":"A utility page that can be used as a tab bar spacer. It allows the user to store notes in markdown and customize the tab title and icon.","id":1,"section":"","tags":null,"title":"Tab","uri":"http://somethingstrange.com/tab/"},{"content":"One of the standout features of Directory Opus 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\u0026rsquo;t regularly closed.\nThe 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.\nFor power users and anyone looking to eliminate the clutter, this script is a game-changer, saving time and improving the overall experience with Directory Opus.\nThe Script The following JavaScript code clears duplicates among the tabs, while ensuring that source, destination, and locked tabs are not closed. Here\u0026rsquo;s an overview of the script:\nThe main function triggers on click, prepares the command, and prioritizes tabs based on whether they\u0026rsquo;re currently selected, locked, or both. Duplicates are flagged and then closed from right to left. Selected and locked tabs are preserved. Temporary flags are used to identify and manage duplicates. Detailed Breakdown Main Function 1 2 3 4 5 6 7 8 9 10 function OnClick(clickData) { DOpus.ClearOutput(); var cmd = clickData.func.command; cmd.deselect = false; var lister = DOpus.listers.lastactive; var sortedTabs = FilterAndSort(lister.tabs); CloseDuplicateTabs(cmd, sortedTabs); } Line 2: Clears the output pane in Directory Opus. Line 3: Retrieves the command object from the click data. Line 4: Ensures the clicked item is not deselected. Line 6: Gets the last active lister (main window) in Directory Opus. Line 7: Filters and sorts the tabs of the active lister. Line 9: Calls a function to close duplicate tabs. Prioritize Selected and Locked Tabs 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function FilterAndSort(collection) { var filteredArray = []; for (var i = 0; i \u0026lt; collection.count; i++) { filteredArray.push(collection[i]); } filteredArray.sort(function(a, b) { if ((a.source \u0026amp;\u0026amp; !b.source) || (a.dest \u0026amp;\u0026amp; !b.dest)) return -1; if ((!a.source \u0026amp;\u0026amp; b.source) || (!a.dest \u0026amp;\u0026amp; b.dest)) return 1; if (a.lock !== \u0026#34;off\u0026#34; \u0026amp;\u0026amp; b.lock === \u0026#34;off\u0026#34;) return -1; if (a.lock === \u0026#34;off\u0026#34; \u0026amp;\u0026amp; b.lock !== \u0026#34;off\u0026#34;) return 1; return 0; }); return filteredArray; } Lines 13-17: Converts the collection of tabs into an array. Lines 19-25: Sorts the tabs based on the following criteria: Source or Destination: Selected (Source or destination) tabs are prioritized. Locked Status: Locked tabs are given priority over unlocked ones. Default Order: Tabs are left in their current order if all other criteria are equal. Close Duplicate Tabs 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 function CloseDuplicateTabs(cmd, tabs) { CreateDuplicateFlag(tabs); for (var i = 0; i \u0026lt; tabs.length; i++) { for (var j = tabs.length - 1; j \u0026gt; i; j--) { var a = tabs[i]; var b = tabs[j]; if (!b.source \u0026amp;\u0026amp; !b.dest \u0026amp;\u0026amp; b.lock === \u0026#34;off\u0026#34; \u0026amp;\u0026amp; a.path.def_value === b.path.def_value) { tabs[j].vars.Set(\u0026#34;isDuplicate\u0026#34;, true); } } } for (var i = tabs.length - 1; i \u0026gt;= 0; i--) { var tab = tabs[i]; if (tab.vars.Get(\u0026#34;isDuplicate\u0026#34;) === true) { cmd.RunCommand(\u0026#34;Go TABCLOSE=\u0026#34; + tab); } } DeleteDuplicateFlag(tabs); } Line 31: Calls a function to add a temporary flag to each tab. This is exclusively used to distinguish between duplicate and non-duplicate tabs. Lines 33-41: Compares each tab with every other tab to find duplicates, excluding source, destination, and locked tabs. Lines 43-48: Closes tabs marked as duplicates, starting from the end of the list. Line 50: Calls a function to remove the temporary flag from each tab. Temporary Duplicate Flag Handling 53 54 55 56 57 58 59 60 61 62 63 function CreateDuplicateFlag(tabs) { for (var i = 0; i \u0026lt; tabs.length; i++) { tabs[i].vars.Set(\u0026#34;isDuplicate\u0026#34;, false); } } function DeleteDuplicateFlag(tabs) { for (var i = 0; i \u0026lt; tabs.length; i++) { tabs[i].vars.Delete(\u0026#34;isDuplicate\u0026#34;); } } Lines 54-56: Adds an isDuplicate flag set to false for each tab. Lines 60-62: Removes the isDuplicate flag from each tab. The Complete Script 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 function OnClick(clickData) { DOpus.ClearOutput(); var cmd = clickData.func.command; cmd.deselect = false; var lister = DOpus.listers.lastactive; var sortedTabs = FilterAndSort(lister.tabs); CloseDuplicateTabs(cmd, sortedTabs); } function FilterAndSort(collection) { var filteredArray = []; for (var i = 0; i \u0026lt; collection.count; i++) { filteredArray.push(collection[i]); } filteredArray.sort(function(a, b) { if ((a.source \u0026amp;\u0026amp; !b.source) || (a.dest \u0026amp;\u0026amp; !b.dest)) return -1; if ((!a.source \u0026amp;\u0026amp; b.source) || (!a.dest \u0026amp;\u0026amp; b.dest)) return 1; if (a.lock !== \u0026#34;off\u0026#34; \u0026amp;\u0026amp; b.lock === \u0026#34;off\u0026#34;) return -1; if (a.lock === \u0026#34;off\u0026#34; \u0026amp;\u0026amp; b.lock !== \u0026#34;off\u0026#34;) return 1; return 0; }); return filteredArray; } function CloseDuplicateTabs(cmd, tabs) { CreateDuplicateFlag(tabs); for (var i = 0; i \u0026lt; tabs.length; i++) { for (var j = tabs.length - 1; j \u0026gt; i; j--) { var a = tabs[i]; var b = tabs[j]; if (!b.source \u0026amp;\u0026amp; !b.dest \u0026amp;\u0026amp; b.lock === \u0026#34;off\u0026#34; \u0026amp;\u0026amp; a.path.def_value === b.path.def_value) { tabs[j].vars.Set(\u0026#34;isDuplicate\u0026#34;, true); } } } for (var i = tabs.length - 1; i \u0026gt;= 0; i--) { var tab = tabs[i]; if (tab.vars.Get(\u0026#34;isDuplicate\u0026#34;) === true) { cmd.RunCommand(\u0026#34;Go TABCLOSE=\u0026#34; + tab); } } DeleteDuplicateFlag(tabs); } function CreateDuplicateFlag(tabs) { for (var i = 0; i \u0026lt; tabs.length; i++) { tabs[i].vars.Set(\u0026#34;isDuplicate\u0026#34;, false); } } function DeleteDuplicateFlag(tabs) { for (var i = 0; i \u0026lt; tabs.length; i++) { tabs[i].vars.Delete(\u0026#34;isDuplicate\u0026#34;); } } The Command File To make adding this command to your Directory Opus installation simpler, the above script has been wrapped into a handy \u0026ldquo;Close Duplicate Tabs\u0026rdquo; Directory Command File (DCF), which includes a label, tooltip, and icon. You can double-click the file to execute it directly, or customize the Directory Opus toolbar and drop it there for easier access.\nYou can download the ready-to-use file, or create it yourself using the following XML code. Just be sure to save the new file with the .dcf file extension so that it\u0026rsquo;s properly handled by Directory Opus.\u0026quot;\nCommand Iconas it appears in Directory\u0026nbsp;Opus\u0026nbsp;13 Close Duplicate Tabs.dcf 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 \u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;button backcol=\u0026#34;none\u0026#34; display=\u0026#34;icon\u0026#34; label_pos=\u0026#34;right\u0026#34; textcol=\u0026#34;none\u0026#34;\u0026gt; \u0026lt;label\u0026gt;Close Duplicate Tabs\u0026lt;/label\u0026gt; \u0026lt;tip\u0026gt;Close duplicate tabs in the active lister\u0026lt;/tip\u0026gt; \u0026lt;icon1\u0026gt;#closetab\u0026lt;/icon1\u0026gt; \u0026lt;function type=\u0026#34;script\u0026#34;\u0026gt; \u0026lt;instruction\u0026gt;@script JScript\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;function OnClick(clickData) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; DOpus.ClearOutput();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var cmd = clickData.func.command;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.deselect = false;\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; var lister = DOpus.listers.lastactive;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var sortedTabs = FilterAndSort(lister.tabs);\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; CloseDuplicateTabs(cmd, sortedTabs);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt;function FilterAndSort(collection) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var filteredArray = [];\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Convert collection to array\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var i = 0; i \u0026amp;lt; collection.count; i++) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; filteredArray.push(collection[i]);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Sort the array\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; filteredArray.sort(function(a, b) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // First criteria: source or dest\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if ((a.source \u0026amp;amp;\u0026amp;amp; !b.source) || (a.dest \u0026amp;amp;\u0026amp;amp; !b.dest)) return -1;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if ((!a.source \u0026amp;amp;\u0026amp;amp; b.source) || (!a.dest \u0026amp;amp;\u0026amp;amp; b.dest)) return 1;\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Second criteria: locked\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (a.lock !== \u0026amp;quot;off\u0026amp;quot; \u0026amp;amp;\u0026amp;amp; b.lock === \u0026amp;quot;off\u0026amp;quot;) return -1;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (a.lock === \u0026amp;quot;off\u0026amp;quot; \u0026amp;amp;\u0026amp;amp; b.lock !== \u0026amp;quot;off\u0026amp;quot;) return 1;\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Default order if all criteria are equal\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; return 0;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; });\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; return filteredArray;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt;function CloseDuplicateTabs(cmd, tabs) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // A temporary \u0026amp;quot;isDuplicate\u0026amp;quot; flag is added to each tab during the\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // removal process\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; CreateDuplicateFlag(tabs);\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Identify the duplicate tabs, while always retaining source, dest, and\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // locked tabs\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var i = 0; i \u0026amp;lt; tabs.length; i++) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var j = tabs.length - 1; j \u0026amp;gt; i; j--) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var a = tabs[i];\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var b = tabs[j];\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (!b.source \u0026amp;amp;\u0026amp;amp; !b.dest \u0026amp;amp;\u0026amp;amp; b.lock === \u0026amp;quot;off\u0026amp;quot; \u0026amp;amp;\u0026amp;amp; a.path.def_value === b.path.def_value) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; tabs[j].vars.Set(\u0026amp;quot;isDuplicate\u0026amp;quot;, true);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Close the duplicate tabs from the end of the collection first. Since\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // the full tab list includes all left tabs followed by all right tabs,\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; // right tab duplicates will be removed first.\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var i = tabs.length - 1; i \u0026amp;gt;= 0; i--) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var tab = tabs[i];\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (tab.vars.Get(\u0026amp;quot;isDuplicate\u0026amp;quot;) === true) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.RunCommand(\u0026amp;quot;Go TABCLOSE=\u0026amp;quot; + tab);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Clear the temporary \u0026amp;quot;isDuplicate\u0026amp;quot; flag from the tabs\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; DeleteDuplicateFlag(tabs);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt;function CreateDuplicateFlag(tabs) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var i = 0; i \u0026amp;lt; tabs.length; i++) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; tabs[i].vars.Set(\u0026amp;quot;isDuplicate\u0026amp;quot;, false);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt;function DeleteDuplicateFlag(tabs) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var i = 0; i \u0026amp;lt; tabs.length; i++) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; tabs[i].vars.Delete(\u0026amp;quot;isDuplicate\u0026amp;quot;);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;/function\u0026gt; \u0026lt;/button\u0026gt; By using this script, you can quickly close duplicate tabs and eliminate unnecessary clutter from your Directory Opus open tab list.\n","description":"Optimize your Directory Opus experience with a script that closes duplicate tabs, keeping your workspace tidy and efficient by retaining only unique tabs. Perfect for saving time and reducing clutter.","id":2,"section":"posts","tags":["directory opus"],"title":"Closing Duplicate Tabs in Directory Opus","uri":"http://somethingstrange.com/posts/closing-duplicate-tabs-in-dopus/"},{"content":"One of the best features of Directory Opus is the ability to have multiple listers with numerous open tabs. However, if you\u0026rsquo;re not diligent in closing unnecessary tabs, you\u0026rsquo;ll quickly end up with a cluttered mix of relevant and irrelevant tabs.\nManaging 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.\nWhether you\u0026rsquo;re a power user or looking to enhance your productivity, this script will save you time and reduce clutter in your tab management.\nNow, let\u0026rsquo;s delve into the specifics of how the script works and the benefits it brings to your daily use of Directory Opus.\nThe Script The following JavaScript code organizes tabs based on their paths, distinguishing between locked and unlocked tabs. It consists of two functions:\nOnClick: Handles the click event, retrieves the command object, prevents automatic deselection, clears existing files, and calls the sortTabs function for both left and right tabs.\nSortTabs: Sorts tabs by their paths, processes locked and unlocked tabs separately, and generates commands to reposition the tabs in the specified order.\n1 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 function OnClick(clickData) { var cmd = clickData.func.command; cmd.deselect = false; // Prevent automatic deselection cmd.ClearFiles(); SortTabs(cmd, clickData.func.sourcetab.lister.tabsleft); SortTabs(cmd, clickData.func.sourcetab.lister.tabsright); } function SortTabs(cmd, tabList) { var setLabels = DOpus.Create.StringSet(); var mapLabelToTabVec = DOpus.Create.Map(); for (var eTabs = new Enumerator(tabList); !eTabs.atEnd(); eTabs.moveNext()) { var tab = eTabs.item(); var label = tab.path.def_value.toLowerCase(); setLabels.insert(label); if (!mapLabelToTabVec.exists(label)) { mapLabelToTabVec(label) = DOpus.Create.Vector(); } mapLabelToTabVec(label).push_back(tab); } var vecLabels = DOpus.Create.Vector(); for (var eLabels = new Enumerator(setLabels); !eLabels.atEnd(); eLabels.moveNext()) { vecLabels.push_back(eLabels.item()); } vecLabels.sort(); cmd.Clear(); var tabPos = 0; // Locked Tabs for (var eLabels = new Enumerator(vecLabels); !eLabels.atEnd(); eLabels.moveNext()) { var vecTabs = mapLabelToTabVec(eLabels.item()); for (var eTabs = new Enumerator(vecTabs); !eTabs.atEnd(); eTabs.moveNext()) { var tab = eTabs.item(); if (tab.lock !== \u0026#34;off\u0026#34;) { cmd.AddLine(\u0026#34;Go TABPOS=\u0026#34; + tabPos + \u0026#34;,\u0026#34; + tab); ++tabPos; } } } // Unlocked Tabs for (var eLabels = new Enumerator(vecLabels); !eLabels.atEnd(); eLabels.moveNext()) { var vecTabs = mapLabelToTabVec(eLabels.item()); for (var eTabs = new Enumerator(vecTabs); !eTabs.atEnd(); eTabs.moveNext()) { var tab = eTabs.item(); if (tab.lock === \u0026#34;off\u0026#34;) { cmd.AddLine(\u0026#34;Go TABPOS=\u0026#34; + tabPos + \u0026#34;,\u0026#34; + tab); ++tabPos; } } } if (tabPos \u0026gt; 1) { cmd.Run(); } } Explanation of the JavaScript Code The provided JavaScript code sorts tabs in Directory Opus based on their paths. Let\u0026rsquo;s break down the code step by step and call out noteworthy functions and behaviors using line numbers:\nFunction OnClick(clickData)\nLine 1: Defines the OnClick function, which is triggered when a button is clicked. Line 2: Retrieves the command object associated with the click. Line 3: Prevents automatic deselection of the clicked item. Line 4: Clears any existing files from the command object. Calling SortTabs Function\nLines 6-7: Calls the SortTabs function twice, once for the left tabs and once for the right tabs of the lister. Function SortTabs(cmd, tabList)\nLine 10: Defines the SortTabs function. Line 11: Creates a set to store unique tab labels. Line 12: Creates a map to associate labels with their corresponding tabs. Looping Through Tabs\nLines 14-21: Loops through each tab in the provided tabList. For each tab, it converts the tab path to lowercase, inserts the label into the set, and adds the tab to the map. Line 22: Converts the set of labels to a vector for sorting. Lines 24-26: Sorts the labels vector alphabetically. Sorting and Positioning Tabs\nLine 28: Clears the command object. Line 29: Initializes the tab position counter. Processing Locked Tabs\nLines 32-40: Loops through the sorted labels and processes tabs that are locked (i.e., lock !== \u0026quot;off\u0026quot;). Adds commands to move these tabs to their new positions. Processing Unlocked Tabs\nLines 43-51: Loops through the sorted labels and processes tabs that are unlocked (i.e., lock === \u0026quot;off\u0026quot;). Adds commands to move these tabs to their new positions. Executing Commands\nLine 53: Runs the commands if there are more than one tab. Customizing the JavaScript Sort by tab label instead of path\nUpdate Line 16 by replacing path.def_value with displayed_label so that you end up with: 16 var label = tab.displayed_label.toLowerCase(); Sort locked and unlocked tabs together\nReplace Lines 36-58 with a single for loop that simply sorts the tabs regardless of whether they\u0026rsquo;re locked or not: 36 37 38 39 40 41 42 43 for (var eLabels = new Enumerator(vecLabels); !eLabels.atEnd(); eLabels.moveNext()) { var vecTabs = mapLabelToTabVec(eLabels.item()); for (var eTabs = new Enumerator(vecTabs); !eTabs.atEnd(); eTabs.moveNext()) { var tab = eTabs.item(); cmd.AddLine(\u0026#34;Go TABPOS=\u0026#34; + tabPos + \u0026#34;,\u0026#34; + tab); ++tabPos; } } The Command File To simplify adding this command to your Directory Opus installation, download the complete \u0026ldquo;Sort Tabs by Path\u0026rdquo; file, which includes an appropriate label, tooltip, and icon. Once downloaded, you can simply double-click the file to execute the command in Directory Opus. You can also customize your toolbar and drag the file to where you want it to appear for easier access.\nIf you\u0026rsquo;d prefer to create the file manually, I\u0026rsquo;ve also included the file contents below. A .dcf file is essentially an XML file associated with Directory Opus, which you can edit using any plain text editor. Simply copy the XML code into a new file and save it with the .dcf file extension.\nCommand Iconas it appears in Directory\u0026nbsp;Opus\u0026nbsp;13 Sort Tabs by Path.dcf 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 \u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;button backcol=\u0026#34;none\u0026#34; display=\u0026#34;icon\u0026#34; label_pos=\u0026#34;right\u0026#34; textcol=\u0026#34;none\u0026#34;\u0026gt; \u0026lt;label\u0026gt;Sort Tabs by Path\u0026lt;/label\u0026gt; \u0026lt;tip\u0026gt;Sort tabs by their lock state and then by their associated file or folder path\u0026lt;/tip\u0026gt; \u0026lt;icon1\u0026gt;#sort_alpha\u0026lt;/icon1\u0026gt; \u0026lt;function type=\u0026#34;script\u0026#34;\u0026gt; \u0026lt;instruction\u0026gt;@script JScript\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;function OnClick(clickData) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var cmd = clickData.func.command;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.deselect = false; // Prevent automatic deselection\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.ClearFiles();\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; sortTabs(cmd, clickData.func.sourcetab.lister.tabsleft);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; sortTabs(cmd, clickData.func.sourcetab.lister.tabsright);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt;function sortTabs(cmd, tabList) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var setLabels = DOpus.Create.StringSet();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var mapLabelToTabVec = DOpus.Create.Map();\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; for (var eTabs = new Enumerator(tabList); !eTabs.atEnd(); eTabs.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var tab = eTabs.item();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var label = tab.path.def_value.toLowerCase();\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; setLabels.insert(label);\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; if (!mapLabelToTabVec.exists(label)) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; mapLabelToTabVec(label) = DOpus.Create.Vector();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; mapLabelToTabVec(label).push_back(tab);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; var vecLabels = DOpus.Create.Vector();\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; for (var eLabels = new Enumerator(setLabels); !eLabels.atEnd(); eLabels.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; vecLabels.push_back(eLabels.item());\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; vecLabels.sort();\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; cmd.Clear();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var tabPos = 0;\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Locked Tabs\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var eLabels = new Enumerator(vecLabels); !eLabels.atEnd(); eLabels.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var vecTabs = mapLabelToTabVec(eLabels.item());\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var eTabs = new Enumerator(vecTabs); !eTabs.atEnd(); eTabs.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var tab = eTabs.item();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (tab.lock !== \u0026amp;quot;off\u0026amp;quot;) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.AddLine(\u0026amp;quot;Go TABPOS=\u0026amp;quot; + tabPos + \u0026amp;quot;,\u0026amp;quot; + tab);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; ++tabPos;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; // Unlocked Tabs\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var eLabels = new Enumerator(vecLabels); !eLabels.atEnd(); eLabels.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var vecTabs = mapLabelToTabVec(eLabels.item());\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; for (var eTabs = new Enumerator(vecTabs); !eTabs.atEnd(); eTabs.moveNext()) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; var tab = eTabs.item();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; if (tab.lock === \u0026amp;quot;off\u0026amp;quot;) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.AddLine(\u0026amp;quot;Go TABPOS=\u0026amp;quot; + tabPos + \u0026amp;quot;,\u0026amp;quot; + tab);\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; ++tabPos;\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction /\u0026gt; \u0026lt;instruction\u0026gt; if (tabPos \u0026amp;gt; 1) {\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; cmd.Run();\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt; }\u0026lt;/instruction\u0026gt; \u0026lt;instruction\u0026gt;}\u0026lt;/instruction\u0026gt; \u0026lt;/function\u0026gt; \u0026lt;/button\u0026gt; By using this script, you can effortlessly organize your Directory Opus tabs, ensuring a neat and efficient workspace. This approach saves time, reduces clutter, and enhances productivity.\n","description":"Streamline your Directory Opus tab management with a script that automatically sorts tabs by their paths, ensuring your most important tabs stay accessible and your workspace remains clutter-free.","id":3,"section":"posts","tags":["directory opus"],"title":"Sorting Tabs in Directory Opus","uri":"http://somethingstrange.com/posts/sorting-tabs-in-dopus/"},{"content":"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\u0026rsquo;s a solution!\nSVGs support the \u0026lt;use\u0026gt; element, which references and duplicates content from another SVG document. This process effectively clones nodes into a hidden DOM and pastes them where the \u0026lt;use\u0026gt; element is, similar to how template elements are cloned.\nCSS-Tricks offers two insightful articles on using SVGs with an external Source or Reference. These are worth a read. Here, I\u0026rsquo;ll be focusing on updating the Font Awesome shortcodes from my earlier article, Hugo with Font Awesome, to work with the \u0026lt;use\u0026gt; element.\nWhat\u0026rsquo;s Covered Here Updating the Font Awesome Partial: Revising the file used by the shortcodes from my earlier article to place an SVG stub on the page, containing the \u0026lt;use\u0026gt; element. Tracking Usage: Keeping track of every SVG stub to ensure we know which styles and icons are used. Adding Source SVGs: At the end of the page, adding a single source SVG for each style/icon pair used earlier. Update the Font Awesome Partial \u0026lt;root\u0026gt;/layouts/partials/fontawesome.html 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 {{- $settings := partial \u0026#34;func/getFontAwesomeSettings.html\u0026#34; (dict \u0026#34;style\u0026#34; .style \u0026#34;arg1\u0026#34; .arg1 \u0026#34;arg2\u0026#34; .arg2) -}} \u0026lt;span style=\u0026#34;line-height:1em; vertical-align:middle;\u0026#34;\u0026gt; {{- $fname:=print \u0026#34;/assets/svg/\u0026#34; .style \u0026#34;/\u0026#34; .icon \u0026#34;.svg\u0026#34; -}} {{- if (fileExists $fname) -}} {{- with .page -}} {{- /* The SVG will utilize the USE tag to reference SVG shapes defined later. This allows multiple SVG instances to share the same shape data. */ -}} {{- $.page.Scratch.Add \u0026#34;used-svg\u0026#34; (slice (print $.style \u0026#34;|\u0026#34; $.icon)) -}} {{- /* The SVG\u0026#39;s original width and height will be retained here, and only the inner content will be replaced with the USE tag. Any customizations, such as size and color should be definined here. Only shape data will be shared. */ -}} {{- $svg := readFile $fname -}} {{- $svg = replace $svg \u0026#34; 512\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34; (print \u0026#34; 512\\\u0026#34; style=\\\u0026#34;height:\u0026#34; $settings.size \u0026#34;; width:\u0026#34; $settings.size \u0026#34;\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34;) -}} {{- $svgStyle := print \u0026#34;--fa-pc:\u0026#34; $settings.primaryColor \u0026#34;;--fa-po:\u0026#34; $settings.primaryOpacity \u0026#34;;\u0026#34; }} {{- if or (eq $.style \u0026#34;duotone\u0026#34;) (eq $.style \u0026#34;custom\u0026#34;) -}} {{- $svgStyle = print $svgStyle \u0026#34;--fa-sc:\u0026#34; $settings.secondaryColor \u0026#34;;--fa-so:\u0026#34; $settings.secondaryOpacity \u0026#34;;\u0026#34; }} {{- end -}} {{- $svg = replaceRE \u0026#34;(\u0026lt;svg.*\u0026gt;)(\u0026lt;!--.*)(\u0026lt;\\\\/svg\u0026gt;)\u0026#34; (print \u0026#34;$1\u0026lt;use xlink:href=\\\u0026#34;#\u0026#34; $.style \u0026#34;-\u0026#34; $.icon \u0026#34;\\\u0026#34; style=\\\u0026#34;\u0026#34; $svgStyle \u0026#34;\\\u0026#34;/\u0026gt;$3\u0026#34;) $svg -}} {{- $svg | safeHTML -}} {{- else -}} {{- $svg := readFile $fname -}} {{- $svg = replace $svg \u0026#34; 512\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34; (print \u0026#34; 512\\\u0026#34; style=\\\u0026#34;height:\u0026#34; $settings.size \u0026#34;; width:\u0026#34; $settings.size \u0026#34;\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34;) -}} {{- $svg = replaceRE \u0026#34;\u0026lt;!--[^\u0026gt;]*--\u0026gt;\u0026#34; \u0026#34;\u0026#34; $svg -}} {{- if eq .style \u0026#34;duotone\u0026#34; -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34; style=\\\u0026#34;fill:\u0026#34; $settings.primaryColor \u0026#34;; opacity:\u0026#34; $settings.primaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34; style=\\\u0026#34;fill:\u0026#34; $settings.secondaryColor \u0026#34;; opacity:\u0026#34; $settings.secondaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;\u0026lt;defs\u0026gt;\u0026lt;style\u0026gt;.fa-secondary{opacity:.4}\u0026lt;/style\u0026gt;\u0026lt;/defs\u0026gt;\u0026#34; \u0026#34;\u0026#34; -}} {{- else -}} {{- $svg = replace $svg \u0026#34;\u0026lt;path\u0026#34; (print \u0026#34;\u0026lt;path fill=\\\u0026#34;\u0026#34; $settings.primaryColor \u0026#34;\\\u0026#34; opacity=\\\u0026#34;\u0026#34; $settings.primaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- end -}} {{- $svg | safeHTML -}} {{- end}} {{- else -}} \u0026lt;span class=\u0026#34;sc-fontawesome-missing\u0026#34; title=\u0026#34;Could not find \u0026amp;quot;{{.icon}}\u0026amp;quot; icon with \u0026amp;quot;{{.style}}\u0026amp;quot; style\u0026#34;\u0026gt;\u0026amp;#xFFFD;\u0026lt;/span\u0026gt; {{- warnf \u0026#34;Could not find \\\u0026#34;%s\\\u0026#34; icon with \\\u0026#34;%s\\\u0026#34; style.\u0026#34; .icon .style -}} {{- end -}} \u0026lt;/span\u0026gt;{{- \u0026#34;\u0026#34; -}} Lines 5-32 changed, however lines 21-31 were just indented, so we can skip those.\nFirst, we check if the partial is being called from a page. If so, we insert the new \u0026lt;use\u0026gt; version; otherwise, we insert the full version with shape data. When inserting the \u0026lt;use\u0026gt; version, we also need to update a scratch with a reference to the style and icon being used, so that a source copy can be added to the page later in the build process. Finally, the SVG file is read and modified with the desired color and opacity settings. Most importantly, all shape data is replaced with the \u0026lt;use\u0026gt; element, resulting in significantly less data being written to the page for this instance compared to including the entire SVG. On a generated page, the above template will result in an \u0026lt;svg\u0026gt; element appearing in the HTML source code. This element will contain only a \u0026lt;use\u0026gt; child element that references the source SVG to obtain its shape data.\n1 2 3 4 5 \u0026lt;span style=\u0026#34;line-height:1em; vertical-align:middle;\u0026#34;\u0026gt; \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 384 512\u0026#34; style=\u0026#34;height:1em; width:1em\u0026#34;\u0026gt; \u0026lt;use xlink:href=\u0026#34;#duotone-file-lines\u0026#34; style=\u0026#34;--fa-pc:grey;--fa-po:1;--fa-sc:white;--fa-so:1;\u0026#34;/\u0026gt; \u0026lt;/svg\u0026gt; \u0026lt;/span\u0026gt; Create the SVG Source Partial Create a new partial file that will insert source copies of all SVG icons previously added to the page with the \u0026lt;use\u0026gt; element. These source copies contain the actual shape data.\n\u0026lt;root\u0026gt;/layouts/partials/body/svg-source.html 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 {{- $all := (.Scratch.Get \u0026#34;used-svg\u0026#34;) | default (slice) }} {{- with $all }} {{- $unique := . | uniq }} \u0026lt;div id=\u0026#34;svg-use-src\u0026#34; style=\u0026#34;display:none\u0026#34;\u0026gt; {{- range $unique }} {{- $tokens := split . \u0026#34;|\u0026#34; }} {{- if eq (len $tokens) 2 }} {{- $style := index $tokens 0 }} {{- $icon := index $tokens 1 }} {{- $fname:=print \u0026#34;/assets/svg/\u0026#34; $style \u0026#34;/\u0026#34; $icon \u0026#34;.svg\u0026#34; -}} {{- if (fileExists $fname) -}} {{- $svg := readFile $fname -}} {{- $svg = replaceRE \u0026#34;(\u0026lt;svg.*\u0026gt;)(\u0026lt;!--.*)(\u0026lt;\\\\/svg\u0026gt;)\u0026#34; (print \u0026#34;$1\u0026lt;g id=\\\u0026#34;\u0026#34; $style \u0026#34;-\u0026#34; $icon \u0026#34;\\\u0026#34;\u0026gt;$2\u0026lt;/g\u0026gt;$3\u0026#34;) $svg -}} {{- $svg = replaceRE \u0026#34;\u0026lt;!--[^\u0026gt;]*--\u0026gt;\u0026#34; \u0026#34;\u0026#34; $svg -}} {{- if or (eq $style \u0026#34;duotone\u0026#34;) (eq $style \u0026#34;custom\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;\u0026lt;defs\u0026gt;\u0026lt;style\u0026gt;.fa-secondary{opacity:.4}\u0026lt;/style\u0026gt;\u0026lt;/defs\u0026gt;\u0026#34; \u0026#34;\u0026#34; -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34; style=\\\u0026#34;fill:var(--fa-pc); opacity:var(--fa-po)\\\u0026#34;\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34; style=\\\u0026#34;fill:var(--fa-sc); opacity:var(--fa-so)\\\u0026#34;\u0026#34;) -}} {{- else -}} {{- $svg = replace $svg \u0026#34;\u0026lt;path\u0026#34; (print \u0026#34;\u0026lt;path fill=\\\u0026#34;var(--fa-pc)\\\u0026#34; opacity=\\\u0026#34;var(--fa-po)\\\u0026#34;\u0026#34;) -}} {{- end }} {{ $svg | safeHTML }} {{- else -}} {{- errorf \u0026#34;Could not find \\\u0026#34;%s\\\u0026#34; icon with \\\u0026#34;%s\\\u0026#34; style.\u0026#34; .icon .style -}} \u0026lt;span class=\u0026#34;sc-fontawesome-missing\u0026#34; title=\u0026#34;Could not find \u0026amp;quot;{{.icon}}\u0026amp;quot; icon with \u0026amp;quot;{{.style}}\u0026amp;quot; style\u0026#34;\u0026gt;\u0026amp;#xFFFD;\u0026lt;/span\u0026gt; {{- end -}} {{- else }} {{- warnf \u0026#34;Not a Font Awesome SVG: \\\u0026#34;%s\\\u0026#34;.\u0026#34; . -}} {{ . }} {{- end }} {{- end }} \u0026lt;/div\u0026gt; {{- end -}} What\u0026rsquo;s going on here?\nAll items are retrieved from the \u0026ldquo;used-svg\u0026rdquo; scratch, which was populated by the fontawesome partial. The collection is filtered so that it only contains unique items. We just want to insert one SVG image per style/icon pair. A hidden \u0026lt;div\u0026gt; is used to contain the SVGs, however it won\u0026rsquo;t be rendered to the page itself. It\u0026rsquo;s only for reference. Finally, a complete SVG icon is inserted for each collection item. Add Source SVGs to the Site The final step is to load the svg-source partial into the site. I do this by adding it to the end of my baseof template, just before the closing \u0026lt;/body\u0026gt; tag.\nYour baseof file may look different. Here\u0026rsquo;s an example:\n\u0026lt;root\u0026gt;/layouts/_default/baseof.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026#34;{{ or .Site.LanguageCode .Site.Language.Lang }}\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;meta name=\u0026#34;viewport\u0026#34; content=\u0026#34;width=device-width, minimum-scale=1.0, initial-scale=1.0\u0026#34;\u0026gt; \u0026lt;title\u0026gt;{{ with .Title }}{{ printf \u0026#34;%s | \u0026#34; . }}{{ end }}{{ site.Title }}\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; {{ block \u0026#34;main\u0026#34; . }} {{ end }} {{- partial \u0026#34;body/svg-source\u0026#34; $.Page }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Once you compile the site, a page featuring multiple Font Awesome SVG icons will have something like this at the bottom:\n1 2 3 4 5 6 7 8 \u0026lt;div id=\u0026#34;svg-use-src\u0026#34; style=\u0026#34;display:none\u0026#34;\u0026gt; \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 384 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;duotone-file-lines\u0026#34;\u0026gt; ... \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 512 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;duotone-folder\u0026#34;\u0026gt; ... \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 384 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;duotone-file-code\u0026#34;\u0026gt; ... \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 512 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;solid-triangle-exclamation\u0026#34;\u0026gt; ... \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 384 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;solid-xmark\u0026#34;\u0026gt; ... \u0026lt;svg xmlns=\u0026#34;http://www.w3.org/2000/svg\u0026#34; viewBox=\u0026#34;0 0 448 512\u0026#34;\u0026gt;\u0026lt;g id=\u0026#34;solid-check\u0026#34;\u0026gt; ... \u0026lt;/div\u0026gt; The above \u0026lt;svg\u0026gt; elements were truncated for brevity. However, you can still see the id attribute of the g child element, which indicates the style and icon the SVG refers to. For example, \u0026lt;g id=\u0026quot;duotone-folder\u0026quot;\u0026gt;.\nBy leveraging the \u0026lt;use\u0026gt; element, you can efficiently add SVG icons to your site while minimizing duplicate data. This approach ensures a clean, optimized, and visually appealing web experience.\n","description":"Boost your website's performance with optimized Font Awesome SVGs, reducing duplication and network calls for faster load times!","id":4,"section":"posts","tags":["font awesome","hugo"],"title":"Using \u003cuse\u003e with Font Awesome SVGs","uri":"http://somethingstrange.com/posts/using-use-with-fontawesome-svgs/"},{"content":" 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 button at the bottom, there's no obvious way of removing items from the list once they're added. Fortunately, there\u0026rsquo;s a configuration file that contains all custom menu entries, and it\u0026rsquo;s a standard Unity YAML data file that can be modified in any plain text editor. The file is called GameViewSizes.asset, and it\u0026rsquo;s located under your user profile.\n%AppData%\\Unity\\Editor-5.x\\Preferences\\GameViewSizes.asset ~/Library/Preferences/Unity/Editor-5.x/GameViewSizes.asset The configuration file is shared by all Unity installations and projects on your computer, so any changes you make to it inside Unity or using a text editor will be accessible the next time you open any Unity project on that computer.\nThe contents of my file are shown below, however the contents of your file will likely differ.\n1 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 %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 \u0026amp;1 MonoBehaviour: m_ObjectHideFlags: 61 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 12330, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: m_Standalone: m_Custom: - m_BaseText: \u0026#34;1.25:1\u0026#34; m_SizeType: 0 m_Width: 5 m_Height: 4 - m_BaseText: \u0026#34;1.33:1\u0026#34; m_SizeType: 0 m_Width: 4 m_Height: 3 - m_BaseText: \u0026#34;1.50:1\u0026#34; m_SizeType: 0 m_Width: 3 m_Height: 2 - m_BaseText: \u0026#34;1.60:1\u0026#34; m_SizeType: 0 m_Width: 16 m_Height: 10 - m_BaseText: \u0026#34;1.78:1\u0026#34; m_SizeType: 0 m_Width: 16 m_Height: 9 - m_BaseText: \u0026#34;2.33:1\u0026#34; m_SizeType: 0 m_Width: 21 m_Height: 9 - m_BaseText: \u0026#34;3.56:1\u0026#34; m_SizeType: 0 m_Width: 32 m_Height: 9 - m_BaseText: m_SizeType: 1 m_Width: 1920 m_Height: 1200 m_iOS: m_Custom: [] m_Android: m_Custom: [] m_HMD: m_Custom: [] Each menu entry consists of four properties:\nProperty Description m_BaseText An optional text label m_SizeType 0 Aspect Ratio (width ∶ height)1 Fixed Resolution (width × height) m_Width The horizontal dimension m_Height The vertical dimension To remove an entry, delete the lines associated with that property group, for example, I could remove the \u0026ldquo;1.50:1\u0026rdquo; (or 3:2) entry by deleting the following lines from the file:\n25 26 27 28 - m_BaseText: \u0026#34;1.50:1\u0026#34; m_SizeType: 0 m_Width: 3 m_Height: 2 Now you know... and knowing is half the battle! ","description":"Modify the GameViewSizes asset to remove custom menu presets.","id":5,"section":"posts","tags":["unity"],"title":"GameView Sizes in Unity","uri":"http://somethingstrange.com/posts/game-view-sizes-in-unity/"},{"content":"Earlier today, I saw a post on Hugo\u0026rsquo;s Discourse site where someone was asking for a way to sort version numbers with a natural sort order where multi-digit numbers are treated atomically.\nThe accepted solution 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.\nI 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\u0026rsquo;t assume the MAJOR.MINOR.PATCH format, it can also handle version strings with pre-release suffixes, such as \u0026ldquo;beta\u0026rdquo; and \u0026ldquo;rc\u0026rdquo;.\n1 2 3 4 5 6 7 8 9 10 11 {{- $versions := slice \u0026#34;17.9.200\u0026#34; \u0026#34;17.1.52\u0026#34; \u0026#34;16.8.100\u0026#34; \u0026#34;3.2.3\u0026#34; \u0026#34;3.1.2\u0026#34; \u0026#34;3.10.0\u0026#34; \u0026#34;17.8.20\u0026#34; \u0026#34;3.9.10\u0026#34; \u0026#34;16.8.93\u0026#34; \u0026#34;16.8.201\u0026#34; \u0026#34;17.8.10\u0026#34; \u0026#34;16.8.25-rc.0\u0026#34; \u0026#34;2.8.2\u0026#34; \u0026#34;16.8.21\u0026#34; \u0026#34;16.8.25-rc.2\u0026#34; \u0026#34;16.8.2\u0026#34; \u0026#34;17.21.46\u0026#34; \u0026#34;17.11.42\u0026#34; \u0026#34;20.8.2\u0026#34; \u0026#34;17.9.26\u0026#34; }} {{- $padded := apply $versions \u0026#34;partial\u0026#34; \u0026#34;padZeroPrefix\u0026#34; \u0026#34;.\u0026#34; }} {{- $sorted := sort $padded \u0026#34;value\u0026#34; \u0026#34;desc\u0026#34; }} {{- $results := apply $sorted \u0026#34;partial\u0026#34; \u0026#34;trimZeroPrefix\u0026#34; \u0026#34;.\u0026#34; }} {{- range $results }} \u0026lt;li\u0026gt;{{- . }}\u0026lt;/li\u0026gt; {{- end }} The original slice has the padZeroPrefix partial function applied over it, which pads all numbers in the string with zeros to the max length of six digits (the max length can be adjusted in the partial). The resulting slice is then sorted in descending order before the trimZeroPrefix is applied to remove the padding. It\u0026rsquo;s simple and clean.\nResults 20.8.2 17.21.46 17.11.42 17.9.200 17.9.26 17.8.20 17.8.10 17.1.52 16.8.201 16.8.100 16.8.93 16.8.25-rc.2 16.8.25-rc.0 16.8.21 16.8.2 3.10.0 3.9.10 3.2.3 3.1.2 2.8.2 padZeroPrefix.html 1 2 3 4 {{- $padSize := 6 }} {{- $paddedString := replaceRE \u0026#34;(\\\\d+)\u0026#34; (print (strings.Repeat (sub $padSize 1) \u0026#34;0\u0026#34;) \u0026#34;$1\u0026#34;) . }} {{- $trimmedString := replaceRE (print \u0026#34;0+(\\\\d{\u0026#34; $padSize \u0026#34;})\u0026#34;) \u0026#34;$1\u0026#34; $paddedString }} {{- return $trimmedString }} The function receives a string as input, prepends 5 zeros in front of every digit sequence it finds within the string, and then trims those numbers down to a maximum of 6 digits before returning the results.\nAs long as the padding length is greater than or equal to the length of the largest number, everything should sort correctly. The following blocks show a few lines of input and how those lines are transformed so that they can be sorted:\nInput: ... 16.8.201 17.8.10 16.8.25-rc.0 2.8.2 16.8.21 ... Output:\n... 000016.000008.000201 000017.000008.000010 000016.000008.000025-rc.000000 000002.000008.000002 000016.000008.000021 ...\ntrimZeroPrefix.html 1 {{- return replaceRE \u0026#34;0+(\\\\d+)\u0026#34; \u0026#34;$1\u0026#34; . }} This function receives a string as input, identifies all digit sequences, and discards unnecessary zeros at the beginning of each sequence before returning the results.\n","description":"A short example on how to sort an array of SemVer strings with a natural sort order.","id":6,"section":"posts","tags":["hugo"],"title":"Natural Sorting of SemVer Strings in Hugo","uri":"http://somethingstrange.com/posts/natural-sorting-of-semver-strings-in-hugo/"},{"content":"Font Awesome is an icon font widely used across various websites, applications, and projects. I\u0026rsquo;ve been utilizing it for years to develop editor tools for my Unity projects. It\u0026rsquo;s truly awesome.\nOne of the first decisions I made as I started building this site with Hugo, 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 articles detail the advantages of SVG icons over font icons, so I won’t repeat those here.\nThe Font Awesome documentation offers various setup methods, from kits to self-hosted options, using web fonts or SVGs with JavaScript. However, I prefer embedding SVGs directly to avoid extra network dependencies and processing delays, as highlighted in a 2018 article by Nick Glabreath. This method results in slightly larger page loads but more efficient performance.\nA more recent article by Micah R Ledbetter builds on Galbreath\u0026rsquo;s work with additional enhancements, which I will further develop.\nQuick and Easy Download Font Awesome I have an active subscription to Font Awesome Pro, so I downloaded the v5.15.4 release from their website. You can download the latest release or the free icon set from the GitHub repository. My version includes 7,864 pro icons and 1,608 free icons.\nCopy the SVGs Some articles suggest placing SVG (scalable vector graphics) files inside your theme folder. However, Hugo\u0026rsquo;s layer-like site generation workflow allows customization without altering the theme code or folder structure. Ideally, nothing should be changed inside the theme folder, so we don\u0026rsquo;t want to place the Font Awesome SVGs there.\nInstead, store the SVGs under assets in the the project root. These files are treated like other assets and only used when the site is generated. The hierarchy looks like this:\n\u0026lt;root\u0026gt; assets svg brands duotone light regular solid If you\u0026rsquo;re using the free version of Font Awesome, you may only have a few of those folders, such as brands and solid.\nAdd the partials Processing the SVG Create a partial file:\n\u0026lt;root\u0026gt;/layouts/partials/fontawesome.html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 {{- $settings := partial \u0026#34;func/getFontAwesomeSettings.html\u0026#34; (dict \u0026#34;style\u0026#34; .style \u0026#34;arg1\u0026#34; .arg1 \u0026#34;arg2\u0026#34; .arg2) -}} \u0026lt;span style=\u0026#34;line-height:1em; vertical-align:middle;\u0026#34;\u0026gt; {{- $fname:=print \u0026#34;/assets/svg/\u0026#34; .style \u0026#34;/\u0026#34; .icon \u0026#34;.svg\u0026#34; -}} {{- if (fileExists $fname) -}} {{- $svg := readFile $fname -}} {{- $svg = replace $svg \u0026#34; 512\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34; (print \u0026#34; 512\\\u0026#34; style=\\\u0026#34;height:\u0026#34; $settings.size \u0026#34;; width:\u0026#34; $settings.size \u0026#34;\\\u0026#34;\u0026gt;\u0026lt;!--\u0026#34;) -}} {{- $svg = replaceRE \u0026#34;\u0026lt;!--[^\u0026gt;]*--\u0026gt;\u0026#34; \u0026#34;\u0026#34; $svg -}} {{- if eq .style \u0026#34;duotone\u0026#34; -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-primary\\\u0026#34; style=\\\u0026#34;fill:\u0026#34; $settings.primaryColor \u0026#34;; opacity:\u0026#34; $settings.primaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34;\u0026#34; (print \u0026#34;class=\\\u0026#34;fa-secondary\\\u0026#34; style=\\\u0026#34;fill:\u0026#34; $settings.secondaryColor \u0026#34;; opacity:\u0026#34; $settings.secondaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- $svg = replace $svg \u0026#34;\u0026lt;defs\u0026gt;\u0026lt;style\u0026gt;.fa-secondary{opacity:.4}\u0026lt;/style\u0026gt;\u0026lt;/defs\u0026gt;\u0026#34; \u0026#34;\u0026#34; -}} {{- else -}} {{- $svg = replace $svg \u0026#34;\u0026lt;path\u0026#34; (print \u0026#34;\u0026lt;path fill=\\\u0026#34;\u0026#34; $settings.primaryColor \u0026#34;\\\u0026#34; opacity=\\\u0026#34;\u0026#34; $settings.primaryOpacity \u0026#34;\\\u0026#34;\u0026#34;) -}} {{- end -}} {{- $svg | safeHTML -}} {{- else -}} \u0026lt;span class=\u0026#34;sc-fontawesome-missing\u0026#34; title=\u0026#34;Could not find \u0026amp;quot;{{.icon}}\u0026amp;quot; icon with \u0026amp;quot;{{.style}}\u0026amp;quot; style\u0026#34;\u0026gt;\u0026amp;#xFFFD;\u0026lt;/span\u0026gt; {{- warnf \u0026#34;Could not find \\\u0026#34;%s\\\u0026#34; icon with \\\u0026#34;%s\\\u0026#34; style.\u0026#34; .icon .style -}} {{- end -}} \u0026lt;/span\u0026gt;{{- \u0026#34;\u0026#34; -}} Note that this partial differs from Ledbetter\u0026rsquo;s in a few ways.\nThis partial was placed outside the theme, so the theme folder path is no longer needed in the $fname variable.\nThe icon style and name are passed to the partial separately and accessed using .style and .icon, respectively.\nOptional size and color (with opacity) settings may now be passed through the shortcodes, and the input is validated with a call to the getFontAwesomeSettings partial, which returns values that are accessed with $settings.\nThe size is applied directly to \u0026lt;svg\u0026gt; using inline styles, which seems to improve layout and sizing with adjacent text.\nThere\u0026rsquo;s now special handling of duotone icons, which have primary and secondary colors and opacities, and those need to be applied to the appropriate \u0026lt;path\u0026gt; in the SVG. The \u0026lt;defs\u0026gt;\u0026lt;style\u0026gt; combo is also removed from the SVG, as its no longer needed and results in a smaller image file size.\nThere\u0026rsquo;s a check to see if the file exists before rendering it. If it doesn\u0026rsquo;t exist, a build warning will be logged and the replacement glyph (�) will be embedded into the page instead. The span wrapping the replacement character also has a tooltip that includes the icon and style names being used, which may be useful for debugging the missing icon. Most importantly, this check allows Hugo to successfully rebuild the page, otherwise the build will fail when readFile fails to load the missing SVG file.\nIf you actually want the build to fail when a missing icon is detected, replace the warnf with an errorf call. That will still stop the build, but produce an error that includes the missing icon information.\nParsing Settings Create a new partial file in the func sub-directory, because this partial will return a value, and it helps to keep partials that return values separate from those that don\u0026rsquo;t.\nA partial that returns a value may only return a value and cannot affect rendering output, so there\u0026rsquo;s no need to worry about whitespace sneaking into the generated HTML when the site is built. \u0026lt;root\u0026gt;/layouts/partials/func/getFontAwesomeSettings.html 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 {{ $lengthPattern := \u0026#34;^[\\\\d\\\\.]+(?i)(?:cm|in|mm|pc|pt|px|q|%|ch|em|ex|lh|rem|vh|vmax|vmin|vw)(?-i)$\u0026#34; }} {{ $data := newScratch }} {{ $data.Set \u0026#34;size\u0026#34; \u0026#34;1em\u0026#34; }} {{ $data.Set \u0026#34;primaryColor\u0026#34; \u0026#34;currentColor\u0026#34; }} {{ $data.Set \u0026#34;primaryOpacity\u0026#34; \u0026#34;1\u0026#34; }} {{ $data.Set \u0026#34;secondaryColor\u0026#34; \u0026#34;currentColor\u0026#34; }} {{ $data.Set \u0026#34;secondaryOpacity\u0026#34; \u0026#34;0.4\u0026#34; }} {{ $args := slice .arg1 .arg2 }} {{ range $args }} {{ $length := findRE $lengthPattern . }} {{ with $length }} {{ $data.Set \u0026#34;size\u0026#34; (delimit $length \u0026#34;\u0026#34;) }} {{ else }} {{ with . }} {{ $parts := split . \u0026#34;;\u0026#34; }} {{ with (index $parts 0) }} {{ $data.Set \u0026#34;primaryColor\u0026#34; ((partial \u0026#34;func/getFontAwesomeColorOpacity.html\u0026#34; (slice . 0)) | default \u0026#34;currentColor\u0026#34;) -}}) }} {{ $data.Set \u0026#34;primaryOpacity\u0026#34; ((partial \u0026#34;func/getFontAwesomeColorOpacity.html\u0026#34; (slice . 1)) | default \u0026#34;1\u0026#34;) -}}) }} {{ end }} {{ with (index $parts 1) }} {{ $data.Set \u0026#34;secondaryColor\u0026#34; ((partial \u0026#34;func/getFontAwesomeColorOpacity.html\u0026#34; (slice . 0)) | default \u0026#34;currentColor\u0026#34;) -}}) }} {{ $data.Set \u0026#34;secondaryOpacity\u0026#34; ((partial \u0026#34;func/getFontAwesomeColorOpacity.html\u0026#34; (slice . 1)) | default \u0026#34;0.4\u0026#34;) -}}) }} {{ end }} {{ end }} {{ end }} {{ end }} {{- return (dict \u0026#34;size\u0026#34; ($data.Get \u0026#34;size\u0026#34;) \u0026#34;primaryColor\u0026#34; ($data.Get \u0026#34;primaryColor\u0026#34;) \u0026#34;primaryOpacity\u0026#34; ($data.Get \u0026#34;primaryOpacity\u0026#34;) \u0026#34;secondaryColor\u0026#34; ($data.Get \u0026#34;secondaryColor\u0026#34;) \u0026#34;secondaryOpacity\u0026#34; ($data.Get \u0026#34;secondaryOpacity\u0026#34;) ) -}} This may seem complex because both the size and color/opacity settings can be provided in any order. This flexibility simplifies shortcode usage. Additionally, the color data is a compound string containing up to two color and two opacity values, which are parsed and separated before being returned.\nI\u0026rsquo;ll run through it quickly.\nA regex pattern is used to identify the size argument, and all length types defined in the CSS specification are included. I only tested with em, rem, and px, but they should all work as expected. A scratch is used to store potential results as the partial arguments are being processed. Default values are established and will be used unless the setting is overridden by an argument. Both arguments are examined, and if the size is identified, it\u0026rsquo;s used, otherwise color parsing is performed. Parsing calls the getFontAwesomeColorOpacity partial, which takes a slice and returns a string, and if parsing fails a default value will be applied. The results are returned in a dict using named parameters. Parsing Color and Opacity Create another func partial for separating color and opacity components.\n\u0026lt;root\u0026gt;/layouts/partials/func/getFontAwesomeColorOpacity.html 1 2 3 4 5 6 7 {{ $result := 0 }} {{ $parts := split (index . 0) \u0026#34;/\u0026#34; }} {{ with (index $parts (index . 1)) }} {{ $result = trim . \u0026#34; \\t\\n\u0026#34; }} {{ end }} {{- return $result -}} This partial is really simple. It takes a slice containing a string and an index. The string may contain both color and opacity data, so it\u0026rsquo;s split into color and opacity. The index just tells the function which setting is requested, and if it exists, it\u0026rsquo;ll be returned, otherwise the function will return 0 as an failure condition.\nThat\u0026rsquo;s it! On to the shortcodes!\nAdd the shortcodes For each shortcode you want to use, create a file at: \u0026lt;root\u0026gt;/layouts/shortcodes/. I created a shortcode for each of the Font Awesome styles (brands, duotone, light, regular, solid). If you\u0026rsquo;re using the free version, you probably won\u0026rsquo;t need them all.\n\u0026lt;root\u0026gt; layouts shortcodes fab.html fad.html fal.html far.html fas.html Start with the shortcode that will handle Font Awesome solid (fas) icons:\n\u0026lt;root\u0026gt;/layouts/shortcodes/fas.html 1 {{ partial \u0026#34;fontawesome.html\u0026#34; (dict \u0026#34;style\u0026#34; \u0026#34;solid\u0026#34; \u0026#34;icon\u0026#34; ( .Get 0 ) \u0026#34;arg1\u0026#34; ( .Get 1 ) \u0026#34;arg2\u0026#34; ( .Get 2 )) }} To handle the brand icons, just change the style parameter to brands in the next shortcode.\n\u0026lt;root\u0026gt;/layouts/shortcodes/fab.html 1 {{ partial \u0026#34;fontawesome.html\u0026#34; (dict \u0026#34;style\u0026#34; \u0026#34;brands\u0026#34; \u0026#34;icon\u0026#34; ( .Get 0 ) \u0026#34;arg1\u0026#34; ( .Get 1 ) \u0026#34;arg2\u0026#34; ( .Get 2 )) }} If you\u0026rsquo;re using the Pro version of Font Awesome, be sure also create the fad, fal, and far shortcode files with the duotone, light, and regular styles, respectively.\nShortcode Usage Syntax {{\u0026lt; \u0026lt;style-shortcode\u0026gt; \u0026lt;icon\u0026gt; [color] [size] \u0026gt;}}\nParameters Notation Description \u0026lt;style-shortcode\u0026gt; One of the Font Awesome shortcodes created earlier: fab, fad, fal, far, or fas. \u0026lt;icon\u0026gt; A Font Awesome icon code such as rocket-launch. [color] A color pattern, which may include a single color value using standard CSS color notation: [color]\n• orange\n• #ffa500\n• rgb(255, 165, 0)\n• hsl(39, 100%, 50%)\nA color and an opacity: [color][/\u0026lt;opacity\u0026gt;]\n• orange / 50%\n• #ffa500 / 0.5\nTwo colors with optional opacity: [color][/\u0026lt;opacity\u0026gt;][;[color][/\u0026lt;opacity\u0026gt;]\n• orange / 50% ; blue / 100%\n• #ffa500 / 0.5 ; #00f / 1\nTo set opacity without affecting color, leave the color value blank or set it to currentColor:\n• / 50%\n• currentColor / 0.5\nTo set secondary color without affecting the primary, leave the primary color value blank or set it to currentColor:\n• ; blue\n• currentColor ; #00f\nIf the value includes any non-alphanumeric characters, wrap it in quotes. [size] The size value using standard CSS length notation.\nIf the value includes any non-alphanumeric characters, wrap it in quotes. Examples To use in markdown content, just use one of the new shortcodes followed by the icon name.\nfab brands\nfad duotone\nfal light\nfar regular\nfas solid\nYou may specify a color after the icon name using standard CSS color notation, such as a hex, rgb, or hsl value or a color keyword.\n{{\u0026lt; fas jack-o-lantern DarkOrange \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;#ff8c00\u0026#34; \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;rgb(255, 140, 0)\u0026#34; \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;hsl(33, 100%, 50%)\u0026#34; \u0026gt;}} The color opacity may be changed by appending a / and a numeric or percentage value. Whitespace surrounding the / is ignored.\nDo not specify opacity directly in the color value, like you can when using a 4-byte hex value or rgba or hsla with alpha channels. Append it instead.\n#6699FF80 (4 bytes)\nrgba(102 153 255 / 50%)\nhsla(220 100% 70% / 50%)\n#6699FF / 50%\nrgb(102 153 255) / 50%\nhsl(220 100% 70%) / 50%\n{{\u0026lt; fas jack-o-lantern \u0026#34;DarkOrange/0.5\u0026#34; \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;DarkOrange/50%\u0026#34; \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;DarkOrange / 50%\u0026#34; \u0026gt;}} {{\u0026lt; fas jack-o-lantern \u0026#34;rgb(255, 140, 0) / 50%\u0026#34; \u0026gt;}} The size may also be specified using standard CSS length notation.\n{{\u0026lt; fas jack-o-lantern 3rem \u0026gt;}} The order of the optional color and size parameters doesn\u0026rsquo;t matter, as long as they follow the icon parameter.\n{{\u0026lt; fas jack-o-lantern DarkOrange 3rem \u0026gt;}} {{\u0026lt; fas jack-o-lantern 3rem DarkOrange \u0026gt;}} Duotone icons have a slightly different color syntax, because they have both a primary and secondary color. Just separate the colors with a ;. Whitespace surrounding the ; is ignored.\nBy default the secondary color has an opacity of 40%, so you may want to override that as well.\n{{\u0026lt; fad jack-o-lantern \u0026#34;yellow;DarkOrange/1\u0026#34; 3rem \u0026gt;}} {{\u0026lt; fad jack-o-lantern \u0026#34;yellow ; DarkOrange / 100%\u0026#34; 3rem \u0026gt;}} Have fun with icons! Elevate your Font Awesome game! Check out Using \u0026lt;use\u0026gt; with Font Awesome SVGs for advanced SVG optimization techniques, reducing load times, and boosting performance.\n","description":"A few basic steps to integrate Font Awesome into a Hugo site. Includes shortcodes allowing for the selection of style, icon, size, and duotone color. 🎨","id":7,"section":"posts","tags":["font awesome","hugo"],"title":"Hugo with Font Awesome","uri":"http://somethingstrange.com/posts/hugo-with-fontawesome/"},{"content":"The following table describes the notation used to indicate command-line syntax.\nNotation Description Loose Text Required text that must be typed as shown. \u0026lt; … \u0026gt; Placeholder for a required value. [ … ] Set of optional items. { … } Set of required items. You must choose one. | (pipe) Pipe separator for mutually exclusive items. ... (ellipsis) Items that can be repeated and used multiple times. ","description":"A few basic steps to integrate Font Awesome with a Hugo site.","id":8,"section":"posts","tags":["terminal"],"title":"Command-Line Syntax Key","uri":"http://somethingstrange.com/posts/command-line-syntax-key/"},{"content":"Setting up a private Verdaccio registry is generally quite easy, and there are plenty of tutorials and videos covering its setup. Unfortunately, most of those resources focus on setting up Verdaccio on a local system, and when docker is involved the tutorials usually assume docker is being run from the command line.\nRunning docker containers on a Synology NAS is a completely different process, and I wasn\u0026rsquo;t able to find even a single resource that covered the procedure. This post will hopefully clear some things up.\nDocker First things first, you\u0026rsquo;ll need to install the Docker package if it\u0026rsquo;s not already installed.\nDocker is a lightweight virtualization application that gives you the ability to run thousands of containers created by developers from all over the world on DSM. The hugely popular built-in image repository, Docker Hub, allows you to find shared applications from other talented developers.\n— Synology DSM Feature: Docker Open Synology Package Center.\nSearch for docker, select it, and click Install.\nFor more in-depth information, see How to use Docker on a Synology NAS.\nFolder Setup When working with Docker containers, I try to handle them like read-only objects whenever possible. If the container needs to work with persistent data, that data should be stored outside the container.\nIt\u0026rsquo;s common for containers to provide \u0026ldquo;mount paths\u0026rdquo; that can be used to map file system folders to the container. The container will then store data in those mapped folders. Later, if you decide to update or replace the container, the persistent data will remain in these folders.\nDon\u0026rsquo;t store data in containers - A container can be stopped, destroyed, or replaced. An application version 1.0 running in container should be easily replaced by the version 1.1 without any impact or loss of data. For that reason, if you need to store data, do it in a volume. In this case, you should also take care if two containers write data on the same volume as it could cause corruption. Make sure your applications are designed to write to a shared data store.\n— 10 things to avoid in docker containers, #1 Tip The Verdaccio container exposes three mount paths (conf, plugins, and storage), and we\u0026rsquo;re going to set those folders up first, so that they\u0026rsquo;re ready to use when Verdaccio is configured later.\nCreate a Shared Folder called docker. It\u0026rsquo;s assumed you\u0026rsquo;ve already created shared folders on your NAS and know how it\u0026rsquo;s done.\nConsider using the following configuration options:\nDisable: Advanced Permissions, Recycle Bin, and File Compression. Enable: Data Integrity Protection. Using File Station, create the following folder hierarchy, which will be used by the container:\ndocker verdaccio conf plugins storage Download Verdaccio Download the latest 5.x container, which as of 2022-03-01 is 5.6.2.\nVersion 6 (Development branch)\n— GitHub / Verdaccio Open Docker and go to the Registry tab.\nSearch for verdaccio/verdaccio, select it, and click Download.\nSelect 5 from the Please Choose a Tag dropdown and click Select.\nInstall Verdaccio In Docker, go to the Image tab.\nLaunch verdaccio/verdaccio:5 image with the following settings:\nChange the Container Name name, as desired. The default name is verdaccio-verdaccio. Check Enable resource limitation, if you want to control how much memory the container will use. My NAS has 8 GB of RAM, so I set the Memory Limit to 2048 MB.\nClick Advanced Settings and go to the Advanced Settings tab, if it\u0026rsquo;s not already selected.\nCheck Enable auto-restart so that the container will restart after an improper shutdown.\nGo to the Volume tab.\nFor each of the three folders created earlier, click Add Folder, select the folder, and enter the Mount Path.\nGo to the Port Settings tab.\nChange the Local Port to match the Container Port value, which in this case is 4873.\nClick Apply, then Next, and finally Apply.\nRun Verdaccio The container should automatically start after applying settings. You can Start, Stop, and Restart it from the Container tab in Docker.\nVerdaccio has a web user interface to display packages in the registry.\nThe web interface can be accessed at http://\u0026lt;nas_ip_address\u0026gt;:4873/.\nVerdaccio has a web user interface to display only the private packages and can be customised to your liking.\n— Verdaccio, Web User Interface Terminal Commands The npm executable provides command line access to the registry, and it allows you to add users, login, logout, set the target registry, publish packages, and more.\nMany commands accept an option to target the registry, such as:\nnpm adduser --registry http://\u0026lt;nas_ip_address\u0026gt;:4873/\nYou can also set the registry so that it\u0026rsquo;s automatically used when executing future commands.\n// Sets the registry (storing it in \u0026lt;home_folder\u0026gt;/.npmrc) npm set registry=http://\u0026lt;nas_ip_address\u0026gt;:4873/ // You can now add a user to the registry with out specifying it. npm adduser // Get the current registry npm get registry \u0026gt; http://\u0026lt;nas_ip_address\u0026gt;:4873/ Some Useful Commands ping Ping npm registry login Create or verify a user account. publish Publish a package whoami Display the username that\u0026rsquo;s currently logged in. help Get help on npm. This will open a webpage with details on the selected command. Authentication The basic Verdaccio setup uses htpasswd for authentication.\nThe authentication is tied to the auth plugin you are using. The package restrictions are also handled by the Package Access.\n— Verdaccio, Authentication Based on config.yaml the file should probably be called htpasswd and not the more common .htpasswd.\nYou can use a Linux command line program create an htpasswd file, otherwise try this:\nGenerate a username:password-hash string using an online htpasswd generator. Only the \u0026ldquo;Apache specific salted MD5 (insecure by common)\u0026rdquo; mode worked for me. Your mileage may vary. It should look similar to this:\nusername:$apr1$wzmb709f$fqeK6wasto.rEa6RhtaMj0\nCreate a htpasswd file inside the verdaccio/storage/ folder.\nIn that file paste the generated htpasswd string, then save the file.\nYou should now be able to log in to your registry from the command line using npm login.\nAuthentication Plugins There are more than a dozen other authentication plugins offered by the Verdaccio community, and some of them look pretty cool.\nIn the near future, I intend to try the Verdaccio GitHub OAuth plugin, which offers GitHub OAuth integration for both the browser and the command line. Login can be limited to members of a certain GitHub organizations, and package access can be limited to GitHub organizations, teams, repos, and users. If I switch over to it, I\u0026rsquo;ll update this post with details.\nPublishing Errors If, when publishing a package in the registry, you receive an error, such as \u0026ldquo;404 Not Found\u0026rdquo; or \u0026ldquo;no such package available\u0026rdquo;, the problem could be related to folder ownership.\nBy default, the conf, plugins, and storage folders are owned by the NAS user that created them, however from what I\u0026rsquo;ve read in some bug reports (#483, #1085), the storage folder and possibly all three should have the owner set to verdaccio or, more importantly, to the UID of 10001.\nI received these errors myself, and changing the owner seemed to solve the problem.\nNow changing the owner of a folder or file to another registered user in Synology DSM is pretty straight forward, but changing it to an unknown user or specific UID is not. For that, I believe you need to SSH into the NAS and change it manually from the command line. That\u0026rsquo;s what I did.\nSSH If you\u0026rsquo;re like me, you probably don\u0026rsquo;t have SSH enabled under normal circumstances, so you\u0026rsquo;ll have to temporarily enable it until the folder ownership has changed.\nTo log into the DSM via SSH:\nEnable SSH via DSM Control Panel \u0026gt; Terminal \u0026amp; SNMP \u0026gt; Enable SSH service.\nFrom the Windows Terminal command line:\nssh \u0026lt;admin_account\u0026gt;@\u0026lt;ip_address\u0026gt;\nSo, if the admin user is \u0026ldquo;bob\u0026rdquo; and the NAS IP address is \u0026ldquo;192.168.1.150\u0026rdquo;, you\u0026rsquo;d use:\nssh [email protected]\nPrior to DSM 6, you could SSH into the NAS using root along with the admin account password, but that\u0026rsquo;s no longer possible. Instead, you can use any account that\u0026rsquo;s in the admin group, and then use sudo to make super user changes. Enter the account\u0026rsquo;s password when prompted.\nYou\u0026rsquo;ll likely start in the user\u0026rsquo;s home folder, so change to the verdaccio folder created earlier with something like:\ncd /volume1/docker/verdaccio/\nYou can view the folder owners using ls -l. The third column will list the folder owner.\nChange the owner of each folder using sudo and chown.\nsudo chown 10001 storage/ sudo chown 10001 plugins/ sudo chown 10001 conf/ Verify that folder ownership has changed then then logout with exit.\nDisable SSH again via DSM Control Panel \u0026gt; Terminal \u0026amp; SNMP \u0026gt; Enable SSH service.\nUnity Scoped Registry Setup The Unity manual covers Scoped Registries and Scoped Registry Authentication, and many other sites provide similar information on how to configure a Unity project to connect to a scoped registry.\nI\u0026rsquo;ll elaborate on the process later, if necessary.\n","description":"","id":9,"section":"posts","tags":["synology","verdaccio","docker","unity","npm"],"title":"Verdaccio, Synology, and Unity. Oh My!","uri":"http://somethingstrange.com/posts/verdaccio-synology-and-unity/"},{"content":"This is a small collection of Unicode characters I sometimes need to copy to the clipboard or reference in some way. It started with just a few whitespace characters, but grew overtime as I added an assortment of dashes, mathematical operators, control codes, and other symbols.\nDashes and Hyphens \u0026#x002D; U+002D HYPHEN-MINUS \u0026ensp;Copy ASCII hyphen, with multiple usage, or “ambiguous semantic value”; the width should be “average”. Sent using the - key. \u0026#x2010; U+2010 HYPHEN \u0026ensp;Copy Unambiguous a hyphen character, as in “top-to-bottom”; narrow width. \u0026#x2011; U+2011 NO-BREAK HYPHEN \u0026ensp;Copy As HYPHEN, but not an allowed line break point. \u0026#x2012; U+2012 FIGURE DASH \u0026ensp;Copy As HYPHEN-MINUS, but has the same width as digits. \u0026#x2013; U+2013 EN DASH \u0026amp;ndash; \u0026ensp;Copy Indicate a range of values. Width is 1/2 em (or 1 en). \u0026#x2014; U+2014 EM DASH \u0026amp;mdash; \u0026ensp;Copy Make a break in the flow of a sentence. Width is 1em. \u0026#x203E; U+203E OVERLINE \u0026amp;oline; \u0026ensp;Copy An overline, overscore, or overbaroverbar, is a typographical feature of a horizontal line drawn immediately above the text. Mathematical Operators \u0026#x2212; U+2212 MINUS SIGN \u0026amp;minus; \u0026ensp;Copy Subtraction arithmetic operator. \u0026#x00B1; U+00B1 PLUS-MINUS SIGN \u0026amp;plusmn; \u0026ensp;Copy Mathematical symbol with multiple meanings, such as an inclusive range of values, a confidence interval, or a measurement uncertainty. \u0026#x00F7; U+00F7 DIVISION SIGN \u0026amp;divide; \u0026ensp;Copy Division arithmetic operator. \u0026#x00D7; U+00D7 MULTIPLICATION SIGN \u0026amp;times; \u0026ensp;Copy Multiplication arithmetic operator. Miscellaneous Symbols \u0026#x00B0; U+00B0 DEGREE SIGN \u0026amp;deg; \u0026ensp;Copy A typographical symbol used to represent, among other things, degrees of arc and degrees of temperature. \u0026#x00A9; U+00A9 COPYRIGHT SIGN \u0026amp;copy; \u0026ensp;Copy The symbol used in copyright notices. \u0026#x00AE; U+00AE REGISTERED SIGN \u0026amp;reg; \u0026ensp;Copy The symbol provides notice that the preceding word or symbol is a registered trademark or service mark. \u0026#x2122; U+2122 TRADE MARK SIGN \u0026amp;trade; \u0026ensp;Copy The symbol to indicate that the preceding mark is an unregistered trademark. \u0026#x2026; U+2026 HORIZONTAL ELLIPSIS \u0026amp;hellip; \u0026ensp;Copy The dot dot dot indicates an intentional omission of a word, sentence, or whole section from a text without altering its original meaning. \u0026#x22EE; U+22EE VERTICAL ELLIPSIS \u0026amp;vellip; \u0026ensp;Copy The vertical dot dot dot is useful for showing omissions in matrices, rows, or vertical lists. Also used as a kebab or meatball icon in user interfaces. \u0026#x2261; U+2261 IDENTICAL TO \u0026amp;equiv; \u0026ensp;Copy The triple bar (tribar) has multiple, context-dependent meanings. Most people know it as the hamburger icon in user interfaces. Non-Breaking Whitespace \u0026#xFEFF; U+FEFF ZERO WIDTH NO-BREAK SPACE \u0026ensp;Copy 0 em \u0026#x202F; U+202F NARROW NO-BREAK SPACE \u0026ensp;Copy Depends on font, typically 1/5 or 1/6 em \u0026#x00A0; U+00A0 NO-BREAK SPACE \u0026amp;nbsp; \u0026ensp;Copy Depends on font, typically 1/4 em, but often not adjusted Whitespace \u0026#x0009; U+0009 TAB \\t \u0026ensp;Copy Love it or hate it. Sent using the Tab key. U+000D NEWLINE \\n \u0026ensp;Copy The One, The Only … \\n \u0026#x200B; U+200B ZERO WIDTH SPACE \u0026amp;ZeroWidthSpace; \u0026ensp;Copy 0 em \u0026#x2006; U+2006 SIX-PER-EM SPACE \u0026ensp;Copy 1/6 em \u0026#x2005; U+2005 FOUR-PER-EM SPACE (mid space) \u0026ensp;Copy 1/4 em \u0026#x2004; U+2004 THREE-PER-EM SPACE (thick space) \u0026amp;emsp13; \u0026ensp;Copy 1/3 em \u0026#x2002; U+2002 EN SPACE (nut) \u0026amp;ensp; \u0026ensp;Copy 1/2 em (or 1 en) \u0026#x2003; U+2003 EM SPACE (mutton) \u0026amp;emsp; \u0026ensp;Copy 1 em \u0026#x200A; U+200A HAIR SPACE \u0026amp;hairsp; \u0026ensp;Copy Depends on font, narrower than THIN SPACE \u0026#x2009; U+2009 THIN SPACE \u0026amp;thinsp; \u0026ensp;Copy Depends on font, typically 1/5 em (or sometimes 1/6 em) \u0026#x0020; U+0020 SPACE \u0026ensp;Copy Depends on font, typically 1/4 em, often adjusted. Sent using the Space key. \u0026#x2008; U+2008 PUNCTUATION SPACE \u0026amp;puncsp; \u0026ensp;Copy Depends on font, the width of a period . \u0026#x2007; U+2007 FIGURE SPACE \u0026amp;numsp; \u0026ensp;Copy (Tabular width), Depends on font, the width of digits File Name Alternatives Most operating systems reserve a set of characters that may not be used in filenames. A sample of some of the reserved characters on Windows include /, \\, ?, *, :, |, \u0026quot;, \u0026lt;, and \u0026gt;.\nHere are some potential alternatives. Depending on the font used, some options are better than others.\nSolidus (Slash, Forward Slash) None are particularly good.\n\u0026#x2044; U+2044 FRACTION SLASH \u0026ensp;Copy \u0026#x2215; U+2215 DIVISION SLASH \u0026ensp;Copy \u0026#x29F8; U+29F8 BIG SOLIDUS \u0026ensp;Copy Appears to be too large to be used as an alternative in many fonts, but looks fine when viewed on Windows in File Explorer, Terminal, and the Command Prompt. ̸ U+0338 COMBINING LONG SOLIDUS OVERLAY \u0026ensp;Copy A space followed by this overlay character. Reverse Solidus (Backslash) ⃥ U+20E5 COMBINING REVERSE SOLIDUS OVERLAY \u0026ensp;Copy A space followed by this overlay character. \u0026#x2216; U+2216 SET MINUS \u0026ensp;Copy \u0026#x27CD; U+27CD MATHEMATICAL FALLING DIAGONAL \u0026ensp;Copy \u0026#x29F5; U+29F5 REVERSE SOLIDUS OPERATOR \u0026ensp;Copy \u0026#x29F9; U+29F9 BIG REVERSE SOLIDUS \u0026ensp;Copy Appears to be too large to be used as an alternative in many fonts, but looks fine when viewed on Windows in File Explorer, Terminal, and the Command Prompt. Question Mark \u0026#x203D; U+203D INTERROBANG \u0026ensp;Copy \u0026#x2047; U+2047 DOUBLE QUESTION MARK \u0026ensp;Copy \u0026#x2753; U+2753 BLACK QUESTION MARK ORNAMENT \u0026ensp;Copy Asterisk \u0026#x26B9; U+26B9 SEXTILE \u0026ensp;Copy Possibly the best looking alternative, depending on the font. \u0026#x2217; U+2217 ASTERISK OPERATOR \u0026ensp;Copy \u0026#x066D; U+066D ARABIC FIVE POINTED STAR \u0026ensp;Copy \u0026#x1F7B6; U+1F7B6 MEDIUM SIX SPOKED ASTERISK \u0026ensp;Copy \u0026#x2731; U+2731 HEAVY ASTERISK \u0026ensp;Copy Colon \u0026#xA789; U+A789 MODIFIER LETTER COLON \u0026ensp;Copy Used as a tone letter in some orthographies Budu (Congo), Sabaot (Kenya), and several Papua New Guinea languages.❤️ This can be used in Windows file names, and appears to be the best alternative to the actual colon character.\n\u0026#x05C3; U+05C3 HEBREW PUNCTUATION SOF PASUQ \u0026ensp;Copy May be used as a Hebrew punctuation colon. In RTL (right-to-left) writing systems, this might be the best alternative. \u0026#x0589; U+0589 ARMENIAN FULL STOP \u0026ensp;Copy \u0026#x2236; U+2236 RATIO \u0026ensp;Copy Preferred to U+003A : for denotation of division or scale in mathematical use. \u0026#xFE30; U+FE30 PRESENTATION FORM FOR VERTICAL TWO DOT LEADER \u0026ensp;Copy Vertical Line (Vertical Bar, Pipe) \u0026#x01C0; U+01C0 LATIN LETTER DENTAL CLICK \u0026ensp;Copy Double Quote \u0026#x2033; U+2033 DOUBLE PRIME \u0026ensp;Copy \u0026#x02BA; U+02BA MODIFIER LETTER DOUBLE PRIME \u0026ensp;Copy \u0026#x02EE; U+02EE MODIFIER LETTER DOUBLE APOSTROPHE \u0026ensp;Copy \u0026#x201D; U+201D RIGHT DOUBLE QUOTATION MARK \u0026ensp;Copy \u0026#x201C; U+201C LEFT DOUBLE QUOTATION MARK \u0026ensp;Copy Less Than \u0026#x02C2; U+02C2 MODIFIER LETTER LEFT ARROWHEAD \u0026ensp;Copy Greater Than \u0026#x02C3; U+02C3 MODIFIER LETTER RIGHT ARROWHEAD \u0026ensp;Copy Control Codes The following control code characters were historically used by computer systems to embed additional information or instructions in ASCII strings or test data streams, such a the cursor position or to delineate sections of data.\nSome of these are commonplace, such as the Format Effectors, while others are rarely used today.\nFormat Effectors ␈ U+0008 BS :: Backspace \\b ^H \u0026ensp;Copy Move the cursor one position leftwards. ␉ U+0009 HT :: Horizontal (Character) Tabulation \\t ^I \u0026ensp;Copy Moves the cursor to the next character tab stop. Sent using the Tab key. ␊ U+000A LF :: Line Feed \\n ^J \u0026ensp;Copy On typewriters, printers, and some terminal emulators, moves the cursor down one row without affecting its column position, however it is generally used to indicate end-of-line in text files.On Unix, LF is used on its own to mark end-of-line.In DOS and Windows, LF is used following CR as part of the CR LF end-of-line sequence.Sent using the Enter or Return keys. ␋ U+000B VT :: Vertical (Line) Tabulation \\v ^K \u0026ensp;Copy Position the form at the next line tab stop. ␌ U+000C FF :: Form Feed \\f ^L \u0026ensp;Copy On printers, load the next page. Treated as whitespace in many programming languages, and may be used to separate logical divisions in code. In some terminal emulators, it clears the screen. It still appears in some common plain text files as a page break character. ␍ U+000D CR :: Carriage Return \\r ^M \u0026ensp;Copy Originally used to move the cursor to column zero while staying on the same line, whereas it is now generally used to indicate end-of-line in text files.On systems such as the Commodore 64, Apple II, and classic Mac OS (prior to Mac OS X), CR is used on its own to mark end-of-line.In DOS and Windows, it is used preceding LF as part of the CR LF end-of-line sequence.Sent using the Enter or Return keys. Information Separators Can be used as delimiters to mark fields of data structures. If used for hierarchical levels, US is the lowest level (dividing plain-text data items), while RS, GS, and FS are of increasing level to divide groups made up of items of the level beneath it.\nWhile it\u0026rsquo;s pretty easy to use JSON, XML, or YAML to serialize data, sometimes a less robust solution is okay. For example, you could use : to join a key and value pair and then ; to join multiple pairs together.\nkey1:value1;key2:value2\nThat\u0026rsquo;s simple enough, but what if the key and/or value contains one of those joining characters? Well, you could instead use the US and RS control codes instead to get the job done, since they\u0026rsquo;re far less likely to be used in either the key or value.\nkey1␟value1␞key2␟value2\n␜ U+001C FS :: File Separator ^\\ \u0026ensp;Copy ␝ U+001D GS :: Group Separator ^] \u0026ensp;Copy ␞ U+001E RS :: Record Separator ^^ \u0026ensp;Copy ␟ U+001F US :: Unit Separator ^_ \u0026ensp;Copy Transmission Controls Historically used for message transmission, which may include a header, message text, and post-text footer, or even multiple headings and associated texts.\n␁header␂text␃␄\n␁header␂text␃footer␄\n␁header␂text␁header␂text␃footer␄\n␁ U+0001 SOH :: Start of Heading ^A \u0026ensp;Copy Used to delimits the start of a message header. The header is terminated by STX. ␂ U+0002 STX :: Start of Text ^B \u0026ensp;Copy Used to terminate the message header and mark the start of the message text. The text is terminated by ETX. ␃ U+0003 ETX :: End of Text ^C \u0026ensp;Copy Used to terminate the message text and mark the start of optional \u0026lsquo;post-text\u0026rsquo;, such as a structured footer. Followed by EOT. In keyboard input, often used as a \u0026lsquo;break\u0026rsquo; character (Ctrl+C) to interrupt or terminate a program or process. ␄ U+0004 EOT :: End of Transmission ^D \u0026ensp;Copy Marks the end of a transmitted message. Often used on Unix to indicate end-of-file on a terminal, interpreted by the shell as the command exit or logout. Other transmission control codes are used for back and forth communication between systems, which may involve establishing or terminating connections, handshaking, and the transmission of data.\nA very basic example of a two-way handshake and data transmission goes something like this: A host will send ENQ to the server and wait for a response. When the server receives the packet, it will respond with ACK if it is ready to receive data or NAK if it\u0026rsquo;s not. Once the host receives the ACK, the handshake will complete, and the host will begin sending data one packet at a time. After each packet is sent, the host will wait for an ACK response before sending the next packet. This back and forth would continue until all data is sent. The host ends the transmission by sending EOT.\nThree-way TCP handshaking uses SYN and ACK for synchronizing communications. A client sends a SYN along with a sequence number (X). The server responds by sending its own SYN and sequence number (Y) along with an ACK and the client\u0026rsquo;s sequence number (X). When the client receives the ACK with the correct sequence (X), it responds by sending its own ACK along with the server\u0026rsquo;s sequence number (Y). The handshake is complete with the client knowing the server and the server knowing the client.\n␅ U+0005 ENQ :: Enquiry ^E \u0026ensp;Copy Signal intended to trigger a response at the receiving end, to see if it is still present. ␆ U+0006 ACK :: Acknowledge ^F \u0026ensp;Copy Response to an ENQ, or an indication of successful receipt of a message. ␐ U+0010 DLE :: Data Link Escape ^P \u0026ensp;Copy Cause a limited number of contiguously following octets to be interpreted in some different way, for example as raw data (as opposed to control codes or graphic characters). The details of this are implementation dependent. ␕ U+0015 NAK :: Negative Acknowledge ^U \u0026ensp;Copy Sent by a station as a negative response to the station with which the connection has been set up. In binary synchronous communication protocol, the NAK is used to indicate that an error was detected in the previously received block and that the receiver is ready to accept retransmission of that block. In multipoint systems, the NAK is used as the not-ready reply to a poll. ␖ U+0016 SYN :: Synchronous Idle ^V \u0026ensp;Copy Used in synchronous transmission systems to provide a signal from which synchronous correction may be achieved between data terminal equipment, particularly when no other character is being transmitted. ␗ U+0017 ETB :: End of Transmission Block ^W \u0026ensp;Copy Indicates the end of a transmission block of data when data are divided into such blocks for transmission purposes. If it is not in use for another purpose, IPTC 7901 recommends interpreting ETB as an end of paragraph character. Device Controls These four control codes are reserved for the control of devices, such as the Telex teleprinter, where DC1 (known also as XON) and DC2 were intended for activating the device, while DC3 (known also as XOFF) and DC4 were intended for pausing and turning off the device.\n␑ U+0011 DC1 :: Device Control 1 (XON) ^Q \u0026ensp;Copy ␒ U+0012 DC2 :: Device Control 2 ^R \u0026ensp;Copy ␓ U+0013 DC3 :: Device Control 3 (XOFF) ^S \u0026ensp;Copy ␔ U+0014 DC4 :: Device Control 4 ^T \u0026ensp;Copy Locking Shifts The SO and SI codes were used to convert between 8-bit and 7-bit character codes. In a 7-bit environment, the Shift Out (SO) control would change the meaning of bytes 0x21 through 0x7E (i.e. the graphical codes, excluding the space) to invoke characters from an alternative set, and the Shift In (SI) control would change them back.\n␎ U+000E SO :: Shift Out ^N \u0026ensp;Copy Switch to an alternative character set. ␏ U+000F SI :: Shift In ^O \u0026ensp;Copy Return to regular character set after Shift Out. Others ␀ U+0000 NUL \\0 ^@ \u0026ensp;Copy Often used as a string terminator, especially in the programming language C. ␇ U+0007 BEL :: Bell, Alert \\a ^G \u0026ensp;Copy Originally used to sound an electromechanical bell on the terminal. Later used for a beep on systems that had no physical bell. On some system terminals, it may instead toggle inverse video. ␘ U+0018 CAN :: Cancel ^X \u0026ensp;Copy Indicates that the data preceding it are in error or are to be disregarded. ␙ U+0019 EM :: End of Medium ^Y \u0026ensp;Copy May be used to indicate the end of the used portion (or usable portion) of the storage medium (i.e., paper or magnetic tapes). Alternatively, it may be repurposed as a space indenting the first line of a paragraph. ␚ U+001A SUB :: Substitute ^Z \u0026ensp;Copy In DOS and Windows, it is used to indicate the end of file, both when typing on the terminal, and sometimes in text files stored on disk. ␛ U+001B ESC :: Escape \\e ^[ \u0026ensp;Copy The Esc key on the keyboard will cause this character to be sent on most systems. In device-control protocols (e.g., printers and terminals) it signals that what follows is a special command sequence rather than normal text. ANSI Escape Codes begin with this escape character. ","description":"","id":10,"section":"posts","tags":["unicode"],"title":"A Convenient Caboodle of Unicode Characters","uri":"http://somethingstrange.com/posts/a-convenient-caboodle-of-unicode-characters/"},{"content":"The ScriptingListener plug-in The ScriptingListener plug-in can record JavaScript to a log file for any operation which is actionable.\nInstall the ScriptingListener plug-in:\nQuit Photoshop.\nDownload the ScriptingListener plug-in package. This package contains the ScriptingListener plug-in in the \u0026ldquo;Utilities\u0026rdquo; folder, scripting documentation, and sample scripts.\nWindows:\nScripting Listener Plug-in for Windows macOS:\nScripting Listener Plug-in for macOS (Photoshop 2020 and later)\nScripting Listener Plug-in for macOS (Photoshop 2019 and earlier) After you download the ScriptingListener plug-in package file above, double-click it to decompress it. If asked, extract all the files.\nAfter you unzip the package, you will see three folders:\nDocuments Sample Scripts Scripting Utilities Copy the Utilities folders to the Photoshop plug-ins folder:\nWindows:\nProgram Files\\Adobe\\Adobe Photoshop [version]\\Plug-ins\\\nmacOS:\nApplications\\Adobe Photoshop [version]\\Plug-ins\\\nRelaunch Photoshop.\nAfter installing the ScriptingListener plug-in, steps you perform are recorded as JavaScript to the ScriptingListenerJS.log on the Desktop.\nFor information on the Photoshop Scripting SDK and using the ScriptingListener Plug-in, see the Photoshop Developer Center.\n","description":"Markdown Cheat Sheet. YEAH!","id":11,"section":"posts","tags":["photoshop","js"],"title":"Photoshop Scripting","uri":"http://somethingstrange.com/posts/photoshop-scripting/"},{"content":"Indenting markdown usually isn\u0026rsquo;t difficult, however there could be a few gotchas. Skip down to the end for a quick little Hugo shortcode for inserting indents in markdown.\nThe Problem 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.\nSearch the web for how to \u0026ldquo;indent without adding a bullet or number in markdown\u0026rdquo; and you\u0026rsquo;ll likely come across multiple suggestions that rely on a mix of HTML tags and CSS styles.\nMost suggestions either involve wrapping the target line with a div styled with a margin to create the indent or a ul and li pair styled to hide the bullet. These suggestions usually work when indenting plain text, but for some reason, they were breaking the link.\nFor example:\n1 \u0026lt;div style=\u0026#34;margin-left: 2em;\u0026#34;\u0026gt;A: [Link](http://example.com)\u0026lt;/div\u0026gt; and\n1 \u0026lt;ul\u0026gt;\u0026lt;li style=\u0026#34;list-style-type: none;\u0026#34;\u0026gt;B: [Link](http://example.com)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; Both of the above examples produced a malformed link when the site was built. As shown here, the markdown link was interpreted as plain text.\nA: [Link](http://example.com) B: [Link](http://example.com) The issue can be avoided by inserting a blank line between the opening tags and the link markdown, but it\u0026rsquo;s not pretty.\n1 2 3 \u0026lt;div style=\u0026#34;margin-left: 2em;\u0026#34;\u0026gt; A: [Link](http://example.com)\u0026lt;/div\u0026gt; and\n1 2 3 \u0026lt;ul\u0026gt;\u0026lt;li style=\u0026#34;list-style-type: none;\u0026#34;\u0026gt; B: [Link](http://example.com)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; That worked:\nA: Link\nB: Link\nSome people may not care too much about their site\u0026rsquo;s generated HTML, but if you do, you may find this solution is even less ideal once you view the source in a browser. That blank line not only introduces a new paragraph, but the p is opened after the div/li and isn\u0026rsquo;t closed until after the div/ul are closed.\n1 2 3 4 5 \u0026lt;div style=\u0026#34;margin-left: 2em;\u0026#34;\u0026gt; \u0026lt;p\u0026gt;A: \u0026lt;a href=\u0026#34;http://example.com\u0026#34;\u0026gt;Link\u0026lt;/a\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;ul\u0026gt;\u0026lt;li style=\u0026#34;list-style-type: none;\u0026#34;\u0026gt; \u0026lt;p\u0026gt;B: \u0026lt;a href=\u0026#34;http://example.com\u0026#34;\u0026gt;Link\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt;\u0026lt;/p\u0026gt; While browsers are pretty forgiving and try to understand what\u0026rsquo;s intended, opening and closing HTML tags should be handled like a Last In, First Out (LIFO) stack. Whichever tag you open last should be closed first.\nStack Good: \u0026lt;b\u0026gt;\u0026lt;i\u0026gt;Last In, First Out (LIFO)\u0026lt;/i\u0026gt;\u0026lt;/b\u0026gt;\nQueue Bad: \u0026lt;b\u0026gt;\u0026lt;i\u0026gt;First In, First Out (FIFO)\u0026lt;/b\u0026gt;\u0026lt;/i\u0026gt;\nThe Solution In this case, a better indent solution is to use a simple span styled as an inline-block with an appropriate left margin.\nWrapping the markdown link with the span works fine:\n1 \u0026lt;span style=\u0026#34;display:inline-block; margin-left: 2em;\u0026#34;\u0026gt;C: [Link](http://example.com)\u0026lt;/span\u0026gt; Closing the span before the markdown is even cleaner:\n1 \u0026lt;span style=\u0026#34;display:inline-block; margin-left: 2em;\u0026#34;\u0026gt;\u0026lt;/span\u0026gt;D: [Link](http://example.com) C: Link\nD: Link\nThe Shortcode To avoid having to insert custom individually styled spans everytime I want to indent something, I created a shortcode to simply things.\n\u0026lt;root\u0026gt; layouts shortcodes indent.html 1 2 3 4 5 {{- if isset .Params 0 -}} \u0026lt;span style=\u0026#34;display:inline-block; width:{{ .Get 0 }};\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; {{- else -}} \u0026lt;span style=\u0026#34;display:inline-block; width:2em;\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; {{- end -}} The argument after indent is optional. It will default to 2em. When using a %, wrap the value in quotes.\n1 2 3 4 5 {{\u0026lt; indent \u0026gt;}}E: [Default Indent](#e)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; {{\u0026lt; indent 2em \u0026gt;}}E: [2em Indent](#e)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; {{\u0026lt; indent 32px \u0026gt;}}E: [32px Indent](#e)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; {{\u0026lt; indent 3rem \u0026gt;}}E: [3rem Indent](#e)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; {{\u0026lt; indent \u0026#34;10%\u0026#34; \u0026gt;}}E: [10% Indent](#e)\u0026lt;/li\u0026gt;\u0026lt;/ul\u0026gt; E: Default Indent\nE: 2em Indent\nE: 32px Indent\nE: 3rem Indent\nE: 10% Indent\nEnjoy ","description":"A quick little shortcode for inserting indents in markdown.","id":12,"section":"posts","tags":["markdown","Hugo","shortcode"],"title":"An Indent Shortcode for Hugo","uri":"http://somethingstrange.com/posts/hugo-indent-shortcode/"},{"content":"About Me I grew up in the 80s, during the golden age of arcade video games and the early days of home computing, and it\u0026rsquo;s hard to overstate the influence video games and 8-bit computers had on me.\nA great many hours were spent sitting with my family in the living room, huddled around the only television in the house, to play games like Pitfall, Keystone Kapers and Yar\u0026rsquo;s Revenge on the Atari 2600. One of my favorites was River Raid, with its endless gameplay provided by procedural terrain generation and enemy placement.\nThe Atari was great, but everything changed for me when I received a Commodore 64 for Christmas in 1982. Compared to the Atari, it had superior graphics and sound, an integrated keyboard, and an amazing 64K of RAM. 64K! More importantly, within minutes of plugging the computer in, I was writing my first program in CBM BASIC. From that point on, I was hooked. Not only did I want to play video games, but I wanted to create \u0026rsquo;em.\nA little more than a decade later, I found myself smack-dab in the middle of the game industry when I joined the legendary Looking Glass Studios as a junior game designer. Since then, I\u0026rsquo;ve worked at several game studios, filled many development roles, released more than a dozen games on various platforms, and learned a ton along the way. Some of the games I\u0026rsquo;ve worked on include Thief: The Dark Project, System Shock 2, Eye of Rana, Dark Messiah of Might and Magic, The Bourne Conspiracy, Skylanders: Lost Islands, and Luna and the Moonling.\nWhen I\u0026rsquo;m not hunched over my workstation in a dimly lit room, I enjoy collecting retro consoles, computers, and games from the late 1970s, 80s, and early 90s.\nMy name is Michael Ryan, and I\u0026rsquo;m a professional game developer and retro computing enthusiast.\nLong live the C64!\nREADY.\nÛ ","description":"A little something about me.","id":13,"section":"","tags":null,"title":"Greetings, Programs!","uri":"http://somethingstrange.com/about/"},{"content":"Markdown goes here.\nMarkdown goes here.\nTEST\nEN\nAdded \u0026ldquo;image\u0026rdquo; front matter.\n","description":"","id":19,"section":"showcase","tags":null,"title":"MX vs. ATV Alive","uri":"http://somethingstrange.com/showcase/games/mx-vs-atv-alive/"},{"content":"Markdown goes here.\nMarkdown goes here.\nTEST\nEN\nAdded \u0026ldquo;image\u0026rdquo; front matter.\n","description":"","id":24,"section":"showcase","tags":null,"title":"Thief 2: The Metal Age","uri":"http://somethingstrange.com/showcase/games/thief-2/"},{"content":"Markdown goes here.\nMarkdown goes here.\nTEST\nEN\nAdded \u0026ldquo;image\u0026rdquo; front matter.\n","description":"","id":25,"section":"showcase","tags":null,"title":"Thief Gold","uri":"http://somethingstrange.com/showcase/games/thief-gold/"},{"content":"Markdown goes here.\nMarkdown goes here.\nTEST\nEN\nAdded \u0026ldquo;image\u0026rdquo; front matter.\n","description":"","id":26,"section":"showcase","tags":null,"title":"Thief: The Dark Project","uri":"http://somethingstrange.com/showcase/games/thief/"},{"content":"Gathered from various sources, including The Markdown Guide, GitHub, and others.\nPersonal reference and for testing this site\u0026rsquo;s theme to ensure markdown is rendered correctly.\nThis Markdown cheat sheet provides a quick overview of all the Markdown syntax elements. It can’t cover every edge case, so if you need more information about any of these elements, refer to the reference guides for basic syntax and extended syntax.\nBasic Syntax These are the elements outlined in John Gruber’s original design document. All Markdown applications support these elements.\nParagraphs are separated by a blank line.\nEmphasis is given with italic, bold, and monospace.\n1 Emphasis is given with *italic*, **bold**, and `monospace`. Headings There are six heading levels (h1 -\u0026gt; h6).\nTo insert a heading, start a line with one or more hash (#) characters, followed by a space character and the heading label. The number of hash characters used will determine the heading level.\n# h1 ## h2 ### h3 #### h4 ##### h5 ###### h6 Alternatively, a line of text may be converted to a level 1 heading by placing one or more equals (=) characters on the line beneath it. A level 2 heading can be created by using minus (-) characters instead.\nh1 ================ h2 ---------------- Emphasis This text will be italic\nThis will also be italic\nThis text will be bold\nThis will also be bold\nThis text will have a strikethrough it\nYou can sometimes combine them\n1 2 3 4 5 6 *This text will be italic* _This will also be italic_ **This text will be bold** __This will also be bold__ ~~This text will have a strikethrough it~~ _You **can ~~sometimes~~** combine them_ Block Quotation Block quotes are\nwritten like so.\nThey can span multiple paragraphs,\nif you like.\n\u0026gt; Block quotes are \u0026gt; written like so. \u0026gt; \u0026gt; They can span multiple paragraphs, \u0026gt; if you like. Lists Unordered list items use * or - at the start of the line, and are indented with two or more spaces to increase the level:\nItem 1 Item 2 Item 2a Item 2b 1 2 3 4 * Item 1 * Item 2 * Item 2a * Item 2b Ordered list items are preceded by one or more digits, followed by a . and a space (1. ). Numbering increases with each successive item at the same indent level. Items may be indented with two or more spaces. Increasing the indent level will restart number at the new level.\nFirst item Second item Item 2a Item 2b Item 2b.1 Item 2b.2 Third item \u0026hellip;\nExplicitly resumed previous numbering. 1 2 3 4 5 6 7 8 9 10 11 1. First item 1. Second item 1. Item 2a 1. Item 2b 1. Item 2b.1 1. Item 2b.2 1. Third item ... 4. Explicitly resumed previous numbering. Code Inline code is wrapped with backticks `.\nIn C#, int is a built-in type alias for System.Int32.\n1 In C#, `int` is a built-in type alias for `System.Int32`. A \u0026ldquo;fenced\u0026rdquo; multi-line code block can be inserted by placing triple backticks ``` before and after the code block. The code language can be optionally set, which may influence syntax highlighting.\n1 2 3 4 void DoSomethingCool() { // put some cool code here ... } 1 2 3 4 5 6 ```c# void DoSomethingCool() { // put some cool code here ... } ``` You can also simply indent your code by four spaces, however this method does not allow the language to be set and no syntax highlighting will be shown:\n1 2 3 4 void DoSomethingCool() { // put some cool code here ... } To include a non-code formatted backtick, escape it with a \\.\nThis is a backtick outside of code: `\n1 This is a backtick outside of code: \\` To include a backtick in formatted code, wrap the character in a double-backtick.\nThis is a backtick in code: `\n1 This is a backtick in code: `` ` `` Horizontal Line You can insert a line with ---\n1 --- Link Any URL (like http://example.com/) will be automatically converted into a clickable link.\nInline Use this format [title](https://www.example.com) to set the inline link title and address separately.\nhttp://github.com - automatic!\nHere is a relative link to the local readme file\nAn external link to the unity documentation\nA link to the top of this document (section header)\n1 2 3 4 5 http://github.com - automatic! Here is a relative link to the local [readme](readme.md) file An an external link to the [unity documentation](https://unity3d.com/) An link to [the top](#root) of this document (section header) Reference Place link definitions anywhere in your Markdown document, such as immediately after the paragraph in which they’re used, or together at the end of the document, sort of like footnotes.\nThe optional title attribute must be enclosed in single quotes, double quotes, or parentheses, and it may be placed on the next line, which tends to look better with longer URLs:\n1 2 3 4 5 6 [id]: http://example.com/ \u0026#39;Optional Title Here\u0026#39; [google]: http://google.com/ \u0026#34;Google\u0026#34; [c64]: https://en.wikipedia.org/wiki/Commodore_64 (Commodore 64) Reference the link using the double or single set of square brackets. The reference label is case-insensitive and multiple links can reference the same definition.\nDid you search for that already?\nThe Commodore 64 is the best selling single computer model of all time.\nI \u0026#x1f497; my C64.\n1 2 3 4 Did you [search][google] for that already? The [Commodore 64][c64] is the best selling single computer model of all time. I :heartpulse: my [C64]. Image Images may be inserted using the following markdown syntax.\n![alt text](/path/to/image.png \u0026quot;MouseOver tooltip text\u0026quot;) The \u0026ldquo;alt text\u0026rdquo; is used by screen readers and rendered when the image fails to load. The tooltip text following the image address is optional and will only appear when the mouse cursor hovers over the image.\n1 ![random photo](https://loremflickr.com/320/240 \u0026#34;An image!\u0026#34;) Nesting You can nest items in a list \u0026hellip;\nFirst, get these ingredients:\ncarrots celery lentils Boil some water.\nDump everything in the pot and follow\nthis algorithm:\nfind wooden spoon uncover pot stir cover pot balance wooden spoon precariously on pot handle wait 10 minutes goto first step (or shut off burner when done) Do not bump wooden spoon or it will fall.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 1. First, get these ingredients: * carrots * celery * lentils 2. Boil some water. 3. Dump everything in the pot and follow this algorithm: find wooden spoon uncover pot stir cover pot balance wooden spoon precariously on pot handle wait 10 minutes goto first step (or shut off burner when done) Do not bump wooden spoon or it will fall. Extended Syntax These elements extend the basic syntax by adding additional features. Not all Markdown applications support these elements.\nTable A table consists of one or more columns and rows, as well as a required header row.\nUse pipes (|) to separate columns and three or more hyphens (---) to separate the header row from all other rows. For compatibility, a pipe should also be used at the beginning and end of each row.\nColumn 1 Header Column 2 Header Column 1 Row 1 Column 2 Row 1 Column 1 Row 2 Column 2 Row 2 1 2 3 4 | Column 1 Header | Column 2 Header | | --------------- | --------------- | | Column 1 Row 1 | Column 2 Row 1 | | Column 1 Row 2 | Column 2 Row 2 | Nested in a Blockquote Custom CSS on this site allows table markdown to be used inside a blockquote by wrapping the markup with a div.table-wrapper.\n1 2 3 4 5 6 7 \u0026lt;div class=\u0026#34;table-wrapper\u0026#34;\u0026gt; \u0026gt; | Column 1 Header | Column 2 Header | \u0026gt; | --------------- | --------------- | \u0026gt; | Column 1 Row 1 | Column 2 Row 1 | \u0026gt; | Column 1 Row 2 | Column 2 Row 2 | \u0026lt;/div\u0026gt; Hiding Header Rows While table header rows are required, they may also be hidden by wrapping the markdown with a div.table-wrapper.no-header.\nColumn 1 Header Column 2 Header Column 1 Row 1 Column 2 Row 1 Column 1 Row 2 Column 2 Row 2 Column 1 Header Column 2 Header Column 1 Row 1 Column 2 Row 1 Column 1 Row 2 Column 2 Row 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \u0026lt;div class=\u0026#34;table-wrapper no-header\u0026#34;\u0026gt; \u0026gt; | Column 1 Header | Column 2 Header | \u0026gt; | --------------- | --------------- | \u0026gt; | Column 1 Row 1 | Column 2 Row 1 | \u0026gt; | Column 1 Row 2 | Column 2 Row 2 | \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;table-wrapper no-header\u0026#34;\u0026gt; | Column 1 Header | Column 2 Header | | --------------- | --------------- | | Column 1 Row 1 | Column 2 Row 1 | | Column 1 Row 2 | Column 2 Row 2 | \u0026lt;/div\u0026gt; Aligning Cell Data Header and cell data can be aligned to the left, right, or center of a the column by inserting colons between hyphens and pipes on the header separator line.\n|:--- | Left align with the colon prefix | ---:| Right align with the colon suffix |:---:| Center the data with prefix and suffix colon. Header1 Header2 Header3 Left-aligned text Right-aligned text Centered text column 1 column 2 column 3 1 2 3 4 | Header1 | Header2 | Header3 | | :---------------- | -----------------: | :-----------: | | Left-aligned text | Right-aligned text | Centered text | | column 1 | column 2 | column 3 | Emoji \u0026#x1f648; \u0026#x1f649; \u0026#x1f64a;\n1 :see_no_evil: :hear_no_evil: :speak_no_evil: The Emoji cheat sheet is a useful reference for emoji shorthand codes.\nThe emojify function can be called directly in templates or Inline Shortcodes.\nN.B. The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.\n1 2 3 4 .emoji { font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols; } Footnote Like reference links links, footnote definitions can be placed anywhere in the document, however the id must be prefixed with a caret (^).\nThe footnotes may include multiple paragraphs and other markdown. Simply indent the lines following the definition to include them.\nFootnotes are enumerated in the order they are referenced, so the definition order does not matter. Footnotes that are defined, but not referenced are ignored.\n1 2 3 4 5 6 7 8 9 10 [^first]: This is the first defined footnote. [^second]: This is the second defined footnote. Indent paragraphs to include them in the footnote. `{ my code }` Add as many paragraphs as you like. [^third]: This is the third defined footnote will be unused. Elsewhere in the document, the footnote may be referenced using the same id.\nReference the second defined footnote. 1\nReference the first defined footnote. 2\n1 2 Reference the second defined footnote. [^second] Reference the first defined footnote. [^first] At the end of the document, the footnotes will appear in the order in which they are referenced:\n1. This is the second defined footnote. Indent paragraphs to include them in the footnote. { my code } Add as many paragraphs as you like. ↩︎ 2. This is the first defined footnote. ↩︎ Heading ID A custom id and class can be added to headers.\n#### My Great Heading {#custom-id .custom-class}\n1 #### My Great Heading {#custom-id .custom-class} Definition List To create a definition list, type the term on the first line. On the next line, type a colon followed by a space and the definition.\nFirst Term This is the definition of the first term. Second Term This is one definition of the second term. This is another definition of the second term. 1 2 3 4 5 6 First Term : This is the definition of the first term. Second Term : This is one definition of the second term. : This is another definition of the second term. Task List links, formatting, and tags supported list syntax required (any unordered or ordered list supported) this is a complete item this is an incomplete item Styled HTML Tags Use HTML for other Elements — abbr, sub, sup, kbd, mark\nabbr GIF is a bitmap image format.\nsub H2O\nsup Xn + Yn: Zn\nkbd The standard kbd HTML tag is styled to look like a key. For combo commands, wrap the full command set in a kbd with the .combo class so that it will be styled as a set won\u0026rsquo;t wrap.\nPress Enter when finished.\nPress CTRL+ALT+DEL to access Task Manager.\n1 2 Press \u0026lt;kbd\u0026gt;Enter\u0026lt;/kbd\u0026gt; when finished. Press \u0026lt;span class=\u0026#34;kbd-combo\u0026#34;\u0026gt;\u0026lt;kbd\u0026gt;CTRL\u0026lt;/kbd\u0026gt;+\u0026lt;kbd\u0026gt;ALT\u0026lt;/kbd\u0026gt;+\u0026lt;kbd\u0026gt;DEL\u0026lt;/kbd\u0026gt;\u0026lt;/span\u0026gt; to access Task Manager. This site has a shortcode for simplifying keyboard key combos, which accepts one or more parameters. Parameters with non-alphanumeric args must be quoted, for example:\n\u0026quot;#\u0026quot; -\u0026gt; #\n`\u0026amp;` -\u0026gt; \u0026amp;\n`\u0026quot;` -\u0026gt; \u0026#34;\n\u0026quot;`\u0026quot; -\u0026gt; `\nParameters are converted to title case (tab -\u0026gt; Tab), however some may be formatted for consistent output, such as:\nprint and PrintScreen -\u0026gt; PrtSc\ncaps and CapsLock -\u0026gt; CapsLk\nnum= and NUM = -\u0026gt; Num =\nDOWN and downArrow -\u0026gt; ▼\nWhen more than one parameter is provided, the combination is linked and will not wrap.\nPress ctrl+alt+del to access Task Manager.\n1 Press {{\u0026lt; kbd CONTROL ALT DELETE \u0026gt;}} to access Task Manager. Escf1f2f3f4f5f6f7f8f9f10f11f12\n`1234567890−=BkSp\ntabqwertyuiop[]\\\nCapsLkasdfghjkl;\u0026#39;return\nLShiftzxcvbnm,./RShift\nLCtrlLWinLAltspaceRAltRWinmenuRCtrl\nNumLkNum /Num *Num −\nNum 7Num 8Num 9Num \u0026#43;\nNum 4Num 5Num 6Num =\nNum 1Num 2Num 3\nNum 0Num .enter\n~!@#$%^\u0026amp;*()_\u0026#43;{}|:\u0026#34;\u0026lt;\u0026gt;?\nInshomePgUpDelendPgDnPrtScScrLkpauseSysReqbreak\n▲▼◀▶FnLCmdRCmdLOptROptclearAltGrhelp\nDuplicate keys are removed and recognized modifier keys are sorted for consistency. Apple has explicit guidelines on the modifier order, however things are a little more flexible in Windows. This is the order used by Unity shortcuts, so that\u0026rsquo;s what I\u0026rsquo;m going with.\nCtrlaltshiftWin\nCtrlOptshiftCmd\nControl, Alt, Option, Shift, Windows, Command, Fn\nCtrl+Opt+Cmd+Fn+k+shift\nCtrl+Fn+alt+k+win+shift\n1 2 {{\u0026lt; kbd K OPTION CONTROL FUNCTION COMMAND SHIFT \u0026gt;}} {{\u0026lt; kbd ALT K WIN CONTROL FUNCTION SHIFT \u0026gt;}} mark Most salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nThis is the second defined footnote.\nIndent paragraphs to include them in the footnote.\n{ my code }\nAdd as many paragraphs as you like.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nThis is the first defined footnote.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","description":"An arcane tome of hexes, spells, and formulae that demonstrate the wicked power of markdown.","id":28,"section":"posts","tags":["markdown"],"title":"The Markdown Grimoire","uri":"http://somethingstrange.com/posts/the-markdown-grimoire/"}]